Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 12 from a total of 12 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Set Update Rate ... | 24429464 | 26 days ago | IN | 0 ETH | 0.00000808 | ||||
| Set Update Rate ... | 24429420 | 26 days ago | IN | 0 ETH | 0.00000805 | ||||
| Set Update Rate ... | 24429155 | 26 days ago | IN | 0 ETH | 0.00000986 | ||||
| Set Update Rate ... | 24429121 | 26 days ago | IN | 0 ETH | 0.00000943 | ||||
| Set Update Rate ... | 24429089 | 26 days ago | IN | 0 ETH | 0.00000792 | ||||
| Set Update Rate ... | 24429067 | 26 days ago | IN | 0 ETH | 0.00000517 | ||||
| Set Update Rate ... | 24429066 | 26 days ago | IN | 0 ETH | 0.00000523 | ||||
| Set Update Rate ... | 24429066 | 26 days ago | IN | 0 ETH | 0.00000841 | ||||
| Set Update Rate ... | 24428704 | 26 days ago | IN | 0 ETH | 0.00000911 | ||||
| Set Update Rate ... | 24421596 | 27 days ago | IN | 0 ETH | 0.00000855 | ||||
| Set Update Rate ... | 24421533 | 27 days ago | IN | 0 ETH | 0.00001097 | ||||
| Set LV Lido Vaul... | 24400511 | 30 days ago | IN | 0 ETH | 0.00001188 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
LVLidoVaultUtil
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 1 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity ^0.8.20;
import {ILVLidoVault} from "./interfaces/ILVLidoVault.sol";
import {LVLidoVaultUpkeeper} from "./LVLidoVaultUpkeeper.sol";
// import {AutomationCompatibleInterface} from "@chainlink/contracts/src/v0.8/automation/AutomationCompatible.sol";
// COMMENTED OUT - Replaced by CRE
// import {FunctionsClient} from "@chainlink/contracts/src/v0.8/functions/v1_0_0/FunctionsClient.sol";
// import {FunctionsRequest} from "@chainlink/contracts/src/v0.8/functions/v1_0_0/libraries/FunctionsRequest.sol";
import {IWsteth} from "./interfaces/vault/IWsteth.sol";
import {VaultLib} from "./libraries/VaultLib.sol";
import {IPoolInfoUtils} from "./interfaces/IPoolInfoUtils.sol";
import {IERC20Pool} from "./interfaces/pool/erc20/IERC20Pool.sol";
import {IERC20} from "@balancer/solidity-utils/openzeppelin/IERC20.sol";
import {IWeth} from "./interfaces/vault/IWeth.sol";
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
// CRE Report Receiver Interface (IReceiver per Chainlink CRE spec)
interface IReceiver {
function onReport(bytes calldata metadata, bytes calldata report) external;
}
// contract LVLidoVaultUtil is AutomationCompatibleInterface, Ownable {
contract LVLidoVaultUtil is IReceiver, IERC165, Ownable {
// COMMENTED OUT - Replaced by CRE
// using FunctionsRequest for FunctionsRequest.Request;
ILVLidoVault public LVLidoVault;
LVLidoVaultUpkeeper public lvLidoVaultUpkeeper;
IPoolInfoUtils public constant poolInfoUtils = IPoolInfoUtils(0x30c5eF2997d6a882DE52c4ec01B6D0a5e5B4fAAE);
uint256 stEthPerToken = IWsteth(address(VaultLib.COLLATERAL_TOKEN)).stEthPerToken();
AggregatorV3Interface internal stethUsdPriceFeed;
AggregatorV3Interface internal ethUsdPriceFeed;
uint8 public constant PRICE_FEED_DECIMALS = 8;
// Each top-up tranche is triggered after an additional ≈1.1 % market draw-down
// (1 / leverageFactor when leverageFactor≈15). Three tranches correspond to
// 1.11 %, 2.22 %, 3.33 % cumulative price moves, after which liquidation may be allowed.
uint256 public constant FACTOR_COLLATERAL_INCREASE = 11e15; // 1.1 %
// Exactly three collateral-lender tranches; when the counter reaches 3 we switch to allowKick.
uint256 public constant MAX_TRANCHES = 3;
uint256 public constant lidoClaimDelay = 7 days;
// Rate is set by the LVLidoVault contract
uint256 public upperBoundRate = 0;
uint256 public lowerBoundRate = 0;
// Receipt hash for cryptographic proof verification
// string public receiptHash;
// uint256 public rate = 221e14;
bool public updateRateNeeded = true;
uint256 public s_lastUpkeepTimeStamp;
address public s_forwarderAddress;
// COMMENTED OUT - Chainlink Functions state (replaced by CRE)
// uint256 public s_requestCounter;
// uint64 public s_subscriptionId;
// uint32 public s_fulfillGasLimit;
// bytes32 public s_lastRequestId;
// bytes public s_requestCBOR;
// bytes public s_lastResponse;
// bytes public s_lastError;
// COMMENTED OUT - FunctionsClient(VaultLib.router) removed, replaced by CRE
constructor(address _LVLidoVault) Ownable(msg.sender) {
LVLidoVault = ILVLidoVault(_LVLidoVault);
stethUsdPriceFeed = AggregatorV3Interface(0xCfE54B5cD566aB89272946F602D76Ea879CAb4a8);
ethUsdPriceFeed = AggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);
// Set rate bounds: 0.5% to 10% (in 1e18 scale)
lowerBoundRate = 5e15; // 0.5%
upperBoundRate = 1e17; // 10%
}
modifier onlyForwarder() {
if (msg.sender != s_forwarderAddress && msg.sender != address(this)) {
revert VaultLib.OnlyForwarder();
}
_;
}
modifier onlyLVLidoVault() {
if (msg.sender != address(LVLidoVault)) {
revert VaultLib.Unauthorized();
}
_;
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IReceiver).interfaceId || interfaceId == type(IERC165).interfaceId;
}
function getWstethToWeth(uint256 _amount) public view returns (uint256) {
// WSTETH -> STETH -> USD -> ETH -> USD -> WETH
(, int256 stethPrice,,,) = stethUsdPriceFeed.latestRoundData();
uint256 stethAmount = _amount * IWsteth(VaultLib.COLLATERAL_TOKEN).stEthPerToken() / 1e18;
// STETH -> USD
uint256 stethValueScaled = stethAmount * uint256(stethPrice);
// USD -> ETH = WETH
(, int256 ethPrice,,,) = ethUsdPriceFeed.latestRoundData();
return stethValueScaled / uint256(ethPrice);
}
function checkUpkeep(bytes calldata) public view returns (bool upkeepNeeded, bytes memory performData) {
// Get the rate for the term if it has passed and auction hasn't happened (Ajna debt 0)
IERC20Pool pool = LVLidoVault.pool();
(uint256 debt,,,) = poolInfoUtils.borrowerInfo(address(pool), address(LVLidoVault));
if (updateRateNeeded && block.timestamp > (LVLidoVault.epochStart() + LVLidoVault.termDuration()) && debt > 0) {
upkeepNeeded = true;
performData = abi.encode(221); // Task ID 221: Get new rate
return (upkeepNeeded, performData);
}
// Calculate the market rate rather than the redemption rate
uint256 newRedemptionRate = getWstethToWeth(1e18);
int256 percentageDifferenceRedemptionRate = (
(int256(newRedemptionRate) - int256(LVLidoVault.currentRedemptionRate())) * 1e18
) / int256(LVLidoVault.currentRedemptionRate()); // Drawdown percentage
int256 currentThreshold = LVLidoVault.priceDifferencethreshold()
- int256(FACTOR_COLLATERAL_INCREASE * LVLidoVault.collateralLenderTraunche()); // -1% - 33% * tranche_num
if (
LVLidoVault.epochStarted() && block.timestamp < (LVLidoVault.epochStart() + LVLidoVault.termDuration())
&& debt > 0
) {
uint256 tranchesToTrigger = 0;
int256 checkThreshold = LVLidoVault.priceDifferencethreshold(); // -1%
uint256 collateralLenderTraunche = LVLidoVault.collateralLenderTraunche();
while (
percentageDifferenceRedemptionRate < checkThreshold
&& tranchesToTrigger + collateralLenderTraunche < MAX_TRANCHES
) {
tranchesToTrigger++;
// -1% - 33% * (0 + 1) = -34%
// -1% - 33% * (0 + 2) = -67%
// -1% - 33% * (0 + 3) = -100%
checkThreshold = LVLidoVault.priceDifferencethreshold()
- int256(FACTOR_COLLATERAL_INCREASE * (collateralLenderTraunche + tranchesToTrigger));
}
uint256 totalCLDepositsUnutilized = LVLidoVault.totalCLDepositsUnutilized();
if (
tranchesToTrigger > 0 && tranchesToTrigger + collateralLenderTraunche <= MAX_TRANCHES
&& totalCLDepositsUnutilized > 0
) {
// Equal-sized tranche approach (Josh): add 1/N of remaining protector funds
uint256 remainingTranches = MAX_TRANCHES - collateralLenderTraunche;
if (remainingTranches == 0) remainingTranches = 1;
uint256 collateralToAddToPreventLiquidation =
LVLidoVault.totalCLDepositsUnutilized() / remainingTranches;
if (collateralToAddToPreventLiquidation > 0) {
upkeepNeeded = true;
performData = abi.encode(0); // Task ID 0: Add collateral (Avoid Liquidation)
return (upkeepNeeded, performData);
}
} else if (tranchesToTrigger + collateralLenderTraunche >= MAX_TRANCHES) {
upkeepNeeded = true;
performData = abi.encode(3); // Task ID 3: Allow kick
return (upkeepNeeded, performData);
}
} else if (
LVLidoVault.epochStarted() && block.timestamp > (LVLidoVault.epochStart() + LVLidoVault.termDuration())
&& LVLidoVault.getAllowKick() == false
) {
if (debt == 0) {
return (true, abi.encode(2)); // Auction happened and debt was cleared, queue task ID 2
} else if (!LVLidoVault.fundsQueued()) {
// See if Ajna debt is 0 or not
(uint256 currentDebt,,,) = poolInfoUtils.borrowerInfo(address(LVLidoVault.pool()), address(LVLidoVault));
if (currentDebt == 0) {
upkeepNeeded = true;
performData = abi.encode(2); // Task ID 2: Withdraw funds
return (upkeepNeeded, performData);
}
upkeepNeeded = true;
performData = abi.encode(1); // Task ID 1: End term and queue funds
return (upkeepNeeded, performData);
} else {
// Determine how much ETH can be claimed
uint256 firstIndex = 1;
uint256 lastIndex = VaultLib.LIDO_WITHDRAWAL.getLastCheckpointIndex();
uint256[] memory requestIds = new uint256[](1);
requestIds[0] = LVLidoVault.requestId();
uint256[] memory hints = VaultLib.LIDO_WITHDRAWAL.findCheckpointHints(requestIds, firstIndex, lastIndex);
uint256[] memory claimableEthValues = VaultLib.LIDO_WITHDRAWAL.getClaimableEther(requestIds, hints);
uint256 amount = claimableEthValues[0];
if (amount > 0) {
upkeepNeeded = true;
performData = abi.encode(2); // Task ID 2: Withdraw funds
return (upkeepNeeded, performData);
}
}
}
upkeepNeeded = false;
performData = "";
return (upkeepNeeded, performData);
}
function performUpkeep(bytes calldata performData) public onlyForwarder {
if (performData.length == 0) revert VaultLib.InvalidInput();
uint256 taskId = abi.decode(performData, (uint256));
IERC20Pool pool = LVLidoVault.pool();
(uint256 t1Debt,,,) = poolInfoUtils.borrowerInfo(address(pool), address(LVLidoVault));
if (taskId == 221 && updateRateNeeded && t1Debt > 0) {
// COMMENTED OUT - getRate() replaced by CRE calling fulfillRateFromCRE()
// getRate();
emit VaultLib.TermEnded(LVLidoVault.epochStart() + LVLidoVault.termDuration());
return;
}
(uint256 t0Debt, uint256 collateral,) = pool.borrowerInfo(address(LVLidoVault));
// Add collateral to Ajna pool; Logic for Avoid Liquidations
// Only proceed if we're in an active epoch
if (
LVLidoVault.epochStarted() && block.timestamp < (LVLidoVault.epochStart() + LVLidoVault.termDuration())
&& t1Debt > 0
) {
uint256 newRedemptionRate = getWstethToWeth(1e18);
// Calculate price change as percentage
int256 percentageDifferenceRedemptionRate = (
(int256(newRedemptionRate) - int256(LVLidoVault.currentRedemptionRate())) * 1e18
) / int256(LVLidoVault.currentRedemptionRate());
// Calculate how many tranches should be triggered
uint256 tranchesToTrigger = 0;
int256 checkThreshold = LVLidoVault.priceDifferencethreshold();
// Count how many thresholds have been crossed
// -20% < -1% && 0 +
while (
percentageDifferenceRedemptionRate < checkThreshold
&& tranchesToTrigger + LVLidoVault.collateralLenderTraunche() < MAX_TRANCHES
) {
tranchesToTrigger++;
checkThreshold = LVLidoVault.priceDifferencethreshold()
- int256(FACTOR_COLLATERAL_INCREASE * (LVLidoVault.collateralLenderTraunche() + tranchesToTrigger));
}
if (
tranchesToTrigger > 0 && tranchesToTrigger + LVLidoVault.collateralLenderTraunche() <= MAX_TRANCHES
&& LVLidoVault.totalCLDepositsUnutilized() > 0
) {
// Equal-sized tranche approach (Josh): add 1/N of remaining protector funds
uint256 remainingTranches = MAX_TRANCHES - LVLidoVault.collateralLenderTraunche();
if (remainingTranches == 0) remainingTranches = 1;
uint256 collateralToAddToPreventLiquidation =
LVLidoVault.totalCLDepositsUnutilized() / remainingTranches;
if (collateralToAddToPreventLiquidation > 0) {
LVLidoVault.avoidLiquidation(collateralToAddToPreventLiquidation);
LVLidoVault.setCollateralLenderTraunche(LVLidoVault.collateralLenderTraunche() + tranchesToTrigger);
LVLidoVault.setCurrentRedemptionRate(newRedemptionRate);
}
} else if (tranchesToTrigger + LVLidoVault.collateralLenderTraunche() >= MAX_TRANCHES) {
LVLidoVault.setAllowKick(true);
}
}
// Request withdrawals
else if (
LVLidoVault.epochStarted() && block.timestamp > (LVLidoVault.epochStart() + LVLidoVault.termDuration())
&& !updateRateNeeded && LVLidoVault.getAllowKick() == false
) {
if (taskId == 1 && t1Debt > 0) {
uint256 approxPercentFinalInterest =
(LVLidoVault.rate() * ((block.timestamp - LVLidoVault.epochStart()) + lidoClaimDelay)) / 365 days;
uint256 stethPerWsteth = getWstethToWeth(1e18);
LVLidoVault.setCurrentRedemptionRate(stethPerWsteth);
emit VaultLib.RedemptionRateUpdated(stethPerWsteth);
uint256 approxCTForClaim =
(LVLidoVault.totalBorrowAmount() * (1e18 + uint256(approxPercentFinalInterest))) / stethPerWsteth;
require(
LVLidoVault.approveForProxy(
VaultLib.COLLATERAL_TOKEN, address(VaultLib.LIDO_WITHDRAWAL), approxCTForClaim
),
"Approval failure."
);
// Todo: Debt exceeds borrower leveraged collateral + epoch collateralLender funds (utilized + unutilized)
uint256[] memory amounts = new uint256[](1);
amounts[0] = approxCTForClaim;
uint256 _requestId = LVLidoVault.requestWithdrawalsWstETH(amounts);
emit VaultLib.FundsQueued(_requestId, approxCTForClaim);
}
// Withdraw funds, End epoch - delegated to LVLidoVaultUpkeeper
else if (taskId == 2) {
// Delegate epoch closing to LVLidoVaultUpkeeper to reduce contract size
lvLidoVaultUpkeeper.closeEpoch(t1Debt, collateral);
updateRateNeeded = true;
}
}
}
function performTask() external {
(bool upkeepNeeded, bytes memory performData) = this.checkUpkeep("0x");
if (upkeepNeeded) {
// Temporarily store the forwarder and set it to this contract
address originalForwarder = s_forwarderAddress;
s_forwarderAddress = address(this);
// Make external call to performUpkeep with the memory data
this.performUpkeep(performData);
// Restore original forwarder
s_forwarderAddress = originalForwarder;
}
else {
revert("No upkeep needed");
}
}
// CHAINLINK FUNCTIONS
/**
* @notice Sets the forwarder address for meta-transactions.
* @dev Can only be called by the owner.
* @param forwarderAddress The new forwarder address.
*/
function setForwarderAddress(address forwarderAddress) public {
require(msg.sender == LVLidoVault.owner(), "Only callable by LVLidoVault");
if (forwarderAddress == address(0)) revert VaultLib.InvalidInput(); // Zero address check for security
emit VaultLib.ForwarderAddressUpdated(s_forwarderAddress, forwarderAddress);
s_forwarderAddress = forwarderAddress;
}
/**
* @notice Sets the LVLidoVaultUpkeeper contract address
* @dev Can only be called by the LVLidoVault owner
* @param _upkeeper The address of the LVLidoVaultUpkeeper contract
*/
function setLVLidoVaultUpkeeper(address _upkeeper) public {
require(msg.sender == LVLidoVault.owner(), "Only callable by LVLidoVault");
if (_upkeeper == address(0)) revert VaultLib.InvalidInput();
lvLidoVaultUpkeeper = LVLidoVaultUpkeeper(_upkeeper);
}
/**
* @notice Set updateRateNeeded flag for testing
* @param _needed The new value for updateRateNeeded
*/
function setUpdateRateNeeded(bool _needed) external {
require(msg.sender == LVLidoVault.owner(), "Only callable by LVLidoVault");
updateRateNeeded = _needed;
}
// ============================================================
// COMMENTED OUT - Chainlink Functions (replaced by CRE)
// ============================================================
/*
function setRequest(bytes memory requestCBOR, uint64 _subscriptionId, uint32 _fulfillGasLimit) external {
require(msg.sender == LVLidoVault.owner(), "Only callable by LVLidoVault");
s_subscriptionId = _subscriptionId;
s_fulfillGasLimit = _fulfillGasLimit;
s_requestCBOR = requestCBOR;
}
function getRate() internal {
s_requestCounter = s_requestCounter + 1;
try i_router.sendRequest(
s_subscriptionId, s_requestCBOR, FunctionsRequest.REQUEST_DATA_VERSION, s_fulfillGasLimit, VaultLib.donId
) returns (bytes32 requestId_) {
s_lastRequestId = requestId_;
emit RequestSent(requestId_);
} catch Error(string memory reason) {
emit VaultLib.RequestRevertedWithErrorMsg(reason);
LVLidoVault.updateRate(0);
updateRateNeeded = false;
} catch (bytes memory data) {
emit VaultLib.RequestRevertedWithoutErrorMsg(data);
LVLidoVault.updateRate(0);
updateRateNeeded = false;
}
}
function fulfillRequest(bytes32 requestId, bytes memory response, bytes memory err) internal override {
s_lastRequestId = requestId;
s_lastResponse = response;
s_lastError = err;
s_lastUpkeepTimeStamp = block.timestamp;
if (err.length > 0) {
emit VaultLib.OCRResponse(requestId, response, LVLidoVault.rate(), err);
LVLidoVault.updateRate(0);
updateRateNeeded = false;
return;
}
if (response.length == 0) {
emit VaultLib.OCRResponse(requestId, response, LVLidoVault.rate(), abi.encodePacked("Empty response"));
LVLidoVault.updateRate(0);
updateRateNeeded = false;
return;
}
(uint256 sumLiquidityRates_1e27, uint256 sumVariableBorrowRates_1e27, uint256 numRates) =
abi.decode(response, (uint256, uint256, uint256));
if (numRates == 0) {
emit VaultLib.OCRResponse(requestId, response, LVLidoVault.rate(), abi.encodePacked("Decoded response is empty"));
LVLidoVault.updateRate(0);
updateRateNeeded = false;
return;
}
uint256 rate = (sumLiquidityRates_1e27 + sumVariableBorrowRates_1e27) / (2 * numRates * 1e9);
if (rate < lowerBoundRate || rate > upperBoundRate) {
emit VaultLib.OCRResponse(requestId, response, LVLidoVault.rate(), abi.encodePacked("Rate out of bounds"));
LVLidoVault.updateRate(0);
updateRateNeeded = false;
return;
}
LVLidoVault.updateRate(rate);
updateRateNeeded = false;
emit VaultLib.OCRResponse(requestId, response, rate, err);
}
*/
// ============================================================
// END COMMENTED OUT CHAINLINK FUNCTIONS
// ============================================================
/**
* @notice CRE entry point to update rate (replaces Chainlink Functions)
* @dev Called by CRE workflow via forwarder with pre-fetched rate data
* @param sumLiquidityRates_1e27 Sum of liquidity rates in 1e27 scale
* @param sumVariableBorrowRates_1e27 Sum of variable borrow rates in 1e27 scale
* @param numRates Number of rate samples (days_collected)
*/
function fulfillRateFromCRE(
uint256 sumLiquidityRates_1e27,
uint256 sumVariableBorrowRates_1e27,
uint256 numRates
) external onlyForwarder {
s_lastUpkeepTimeStamp = block.timestamp;
// Handle case with no valid rate data
if (numRates == 0) {
emit VaultLib.OCRResponse(bytes32(0), "", LVLidoVault.rate(), abi.encodePacked("CRE: No rate data"));
LVLidoVault.updateRate(0);
updateRateNeeded = false;
return;
}
// Calculate the average APR:
// 1. Adding supply and borrow rates
// 2. Dividing by 2 to get the average between supply and borrow
// 3. Dividing by numRates to get the average across all protocols
// 4. Converting from 1e27 to 1e18 scale by dividing by 1e9
uint256 rate = (sumLiquidityRates_1e27 + sumVariableBorrowRates_1e27) / (2 * numRates * 1e9);
// Validate rate is within bounds
if (rate < lowerBoundRate || rate > upperBoundRate) {
emit VaultLib.OCRResponse(bytes32(0), "", LVLidoVault.rate(), abi.encodePacked("CRE: Rate out of bounds"));
LVLidoVault.updateRate(0);
updateRateNeeded = false;
return;
}
// Rate is within bounds, proceed with update
LVLidoVault.updateRate(rate);
updateRateNeeded = false;
emit VaultLib.OCRResponse(bytes32(0), abi.encode(sumLiquidityRates_1e27, sumVariableBorrowRates_1e27, numRates), rate, "");
}
// ============================================================
// CRE REPORT RECEIVER
// ============================================================
/// @notice Event emitted when a CRE report is received
event CreReportReceived(uint256 indexed taskId, bytes metadata);
/**
* @notice CRE Report Receiver - entry point for Chainlink Runtime Environment
* @dev Called by CRE DON via writeReport. Decodes the report and dispatches
* to the appropriate handler function based on task ID.
*
* Report encoding format:
* - taskId (uint256): The task to execute
* - 0, 1, 2, 3: Standard upkeep tasks (calls performTask internally)
* - 221: Rate update task (decodes rate data and calls fulfillRateFromCRE)
* - For task 221, additional data:
* - sumLiquidityRates_1e27 (uint256)
* - sumVariableBorrowRates_1e27 (uint256)
* - numRates (uint256)
*
* @param metadata CRE metadata (workflow info, timestamps, etc.)
* @param report The encoded report payload containing task ID and data
*/
function onReport(bytes calldata metadata, bytes calldata report) external override {
// Decode the task ID from the report
if (report.length < 32) {
revert VaultLib.InvalidInput();
}
uint256 taskId = abi.decode(report, (uint256));
emit CreReportReceived(taskId, metadata);
if (taskId == 221) {
// Task 221: Rate update
// Report format: (taskId, sumLiquidityRates_1e27, sumVariableBorrowRates_1e27, numRates)
if (report.length < 128) {
// Not enough data for rate update, use fallback (zeros)
_handleRateUpdate(0, 0, 0);
} else {
(
, // taskId already decoded
uint256 sumLiquidityRates_1e27,
uint256 sumVariableBorrowRates_1e27,
uint256 numRates
) = abi.decode(report, (uint256, uint256, uint256, uint256));
_handleRateUpdate(sumLiquidityRates_1e27, sumVariableBorrowRates_1e27, numRates);
}
} else {
// Tasks 0, 1, 2, 3: Standard upkeep tasks
// Check upkeep and perform if needed
(bool upkeepNeeded, bytes memory performData) = this.checkUpkeep("0x");
if (upkeepNeeded) {
// Temporarily allow this contract to call performUpkeep
address originalForwarder = s_forwarderAddress;
s_forwarderAddress = address(this);
this.performUpkeep(performData);
s_forwarderAddress = originalForwarder;
}
}
}
/**
* @notice Internal handler for rate updates from CRE
* @dev Extracted to avoid code duplication between fulfillRateFromCRE and onReport
*/
function _handleRateUpdate(
uint256 sumLiquidityRates_1e27,
uint256 sumVariableBorrowRates_1e27,
uint256 numRates
) internal {
s_lastUpkeepTimeStamp = block.timestamp;
// Handle case with no valid rate data
if (numRates == 0) {
emit VaultLib.OCRResponse(bytes32(0), "", LVLidoVault.rate(), abi.encodePacked("CRE: No rate data"));
LVLidoVault.updateRate(0);
updateRateNeeded = false;
return;
}
// Calculate the average APR
uint256 rate = (sumLiquidityRates_1e27 + sumVariableBorrowRates_1e27) / (2 * numRates * 1e9);
// Validate rate is within bounds
if (rate < lowerBoundRate || rate > upperBoundRate) {
emit VaultLib.OCRResponse(bytes32(0), "", LVLidoVault.rate(), abi.encodePacked("CRE: Rate out of bounds"));
LVLidoVault.updateRate(0);
updateRateNeeded = false;
return;
}
// Rate is within bounds, proceed with update
LVLidoVault.updateRate(rate);
updateRateNeeded = false;
emit VaultLib.OCRResponse(bytes32(0), abi.encode(sumLiquidityRates_1e27, sumVariableBorrowRates_1e27, numRates), rate, "");
}
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity ^0.8.20;
import {IERC20Pool} from "./pool/erc20/IERC20Pool.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {VaultLib} from "../libraries/VaultLib.sol";
interface ILVLidoVault {
function pool() external view returns (IERC20Pool);
function totalBorrowAmount() external view returns (uint256);
function totalCLDepositsUnutilized() external view returns (uint256);
function totalCLDepositsUtilized() external view returns (uint256);
function epochStartRedemptionRate() external view returns (uint256);
function epochStart() external view returns (uint256);
function termDuration() external view returns (uint256);
function currentRedemptionRate() external view returns (uint256);
function priceDifferencethreshold() external view returns (int256);
function collateralLenderTraunche() external view returns (uint256);
function epochStarted() external view returns (bool);
function getInverseInitialCollateralLenderDebtRatio() external view returns (uint256);
function fundsQueued() external view returns (bool);
function requestId() external view returns (uint256);
function getRate() external;
function avoidLiquidation(uint256 collateralToAdd) external;
function setCollateralLenderTraunche(uint256 newTraunche) external;
function setCurrentRedemptionRate(uint256 newRate) external;
function setAllowKick(bool allow) external;
function rate() external view returns (uint256);
function approveForProxy(address token, address spender, uint256 amount) external returns (bool);
function requestWithdrawalsWstETH(uint256[] calldata amounts) external returns (uint256);
function claimWithdrawal() external;
function depositEthForWeth(uint256 amount) external;
function totalManualRepay() external view returns (uint256);
function wethToWsteth(uint256 amount) external returns (uint256);
function setTotalManualRepay(uint256 newTotal) external;
function testQuoteToken() external view returns (address);
function mintForProxy(address token, address to, uint256 amount) external returns (bool);
function totalLenderQTUtilized() external view returns (uint256);
function totalLenderQTUnutilized() external view returns (uint256);
function repayDebtForProxy(uint256 debtAmount, uint256 collateralAmount) external;
function totalBorrowerCTUnutilized() external view returns (uint256);
function totalCollateralLenderCT() external view returns (uint256);
function getLenderOrders() external view returns (VaultLib.LenderOrder[] memory);
function getLenderOrdersLength() external view returns (uint256);
function getBorrowerOrders() external view returns (VaultLib.BorrowerOrder[] memory);
function getBorrowerOrdersLength() external view returns (uint256);
function getCollateralLenderOrders() external view returns (VaultLib.CollateralLenderOrder[] memory);
function getCollateralLenderOrdersLength() external view returns (uint256);
function clearAjnaDeposits(uint256 depositSize) external;
function testCollateralToken() external view returns (IERC20);
function burnForProxy(address token, address from, uint256 amount) external returns (bool);
function totalBorrowerCT() external view returns (uint256);
function epoch() external view returns (uint256);
function getEpochMatches(uint256 epoch) external view returns (VaultLib.MatchInfo[] memory);
function lenderOrdersPush(VaultLib.LenderOrder memory order) external;
function borrowerOrdersPush(VaultLib.BorrowerOrder memory order) external;
function setTotalLenderQTUnutilized(uint256 amount) external;
function setTotalLenderQTUtilized(uint256 amount) external;
function setTotalBorrowerCT(uint256 amount) external;
function setTotalBorrowerCTUnutilized(uint256 amount) external;
function deleteEpochMatches(uint256 epoch) external;
function getEpochCollateralLenderOrders(uint256 epoch)
external
view
returns (VaultLib.CollateralLenderOrder[] memory);
function collateralLenderOrdersPush(VaultLib.CollateralLenderOrder memory order) external;
function setTotalCollateralLenderCT(uint256 amount) external;
function deleteEpochCollateralLenderOrders(uint256 epoch) external;
function setTotalCLDepositsUnutilized(uint256 amount) external;
function setTotalCLDepositsUtilized(uint256 amount) external;
function end_epoch() external;
function owner() external view returns (address);
function getAllowKick() external view returns (bool);
function updateRate(uint256 _rate) external;
function getEpochAaveCLDeposits(uint256 _epoch) external view returns (uint256);
function getUserAaveCLDeposit(address user, uint256 _epoch) external view returns (uint256);
function getAaveATokenAddress() external view returns (address);
function getAaveBalance() external view returns (uint256);
function getTotalAaveInterestAccrued() external view returns (uint256);
function getEpochAaveLenderDeposits(uint256 _epoch) external view returns (uint256);
function getAaveBalanceQuote() external view returns (uint256);
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity ^0.8.20;
import {ILVLidoVault} from "./interfaces/ILVLidoVault.sol";
import {VaultLib} from "./libraries/VaultLib.sol";
import {IPoolInfoUtils} from "./interfaces/IPoolInfoUtils.sol";
import {IERC20Pool} from "./interfaces/pool/erc20/IERC20Pool.sol";
import {IERC20} from "@balancer/solidity-utils/openzeppelin/IERC20.sol";
/**
* @title LVLidoVaultUpkeeper
* @notice Handles epoch closing logic extracted from LVLidoVaultUtil to reduce contract size
* @dev This contract is called by LVLidoVaultUtil.performUpkeep() for task 2 (close epoch)
* It processes Lido withdrawals, calculates debts, and distributes funds to participants
*/
contract LVLidoVaultUpkeeper {
ILVLidoVault public immutable LVLidoVault;
IPoolInfoUtils public constant poolInfoUtils = IPoolInfoUtils(0x30c5eF2997d6a882DE52c4ec01B6D0a5e5B4fAAE);
address public lvLidoVaultUtil;
error OnlyLVLidoVaultUtil();
error DebtGreaterThanAvailableFunds();
error UpkeepFailed();
modifier onlyUtil() {
if (msg.sender != lvLidoVaultUtil) revert OnlyLVLidoVaultUtil();
_;
}
constructor(address _LVLidoVault) {
LVLidoVault = ILVLidoVault(_LVLidoVault);
}
/**
* @notice Sets the LVLidoVaultUtil address that is authorized to call this contract
* @dev Can only be called by the LVLidoVault owner
* @param _util The address of the LVLidoVaultUtil contract
*/
function setLVLidoVaultUtil(address _util) external {
require(msg.sender == LVLidoVault.owner(), "Only owner");
require(_util != address(0), "Invalid address");
lvLidoVaultUtil = _util;
}
/**
* @notice Closes the current epoch by processing withdrawals, calculating debts, and distributing funds
* @dev This function handles the entire epoch closing process:
* 1. Claims Lido withdrawals if available
* 2. Calculates actual debt based on elapsed time and rate
* 3. Processes debt repayment
* 4. Calculates amounts owed to lenders, borrowers, and collateral lenders
* 5. Creates new orders for the next epoch
* 6. Cleans up epoch data
* @param t1Debt The current debt from pool info (t1 debt)
* @param collateral The current collateral amount from pool
*/
function closeEpoch(uint256 t1Debt, uint256 collateral) external onlyUtil {
IERC20Pool pool = LVLidoVault.pool();
// Determine actual debt based on elapsed time
uint256 timeElapsed = block.timestamp - LVLidoVault.epochStart();
uint256 actualDebt;
if (t1Debt > 0) {
actualDebt = (LVLidoVault.totalBorrowAmount() * (1e18 + ((LVLidoVault.rate() * timeElapsed) / 365 days))) / 1e18;
} else {
actualDebt = 0;
}
uint256 claimAmount = 0;
if (LVLidoVault.fundsQueued()) {
claimAmount = _processLidoWithdrawal(t1Debt);
}
// Process debt and calculate amounts owed
uint256 matchedLendersOwed = _processDebtAndCalculateOwed(
t1Debt,
actualDebt,
claimAmount,
pool
);
// Repay debt if needed
if (t1Debt > 0 || collateral > 0) {
LVLidoVault.repayDebtForProxy(t1Debt, collateral);
}
// Calculate collateral lenders owed (0.14% APY)
uint256 matchedCollateralLendersOwed = _calculateCollateralLendersOwed(timeElapsed);
// Calculate borrowers owed
uint256 matchedBorrowersOwed = _calculateBorrowersOwed(matchedCollateralLendersOwed);
emit VaultLib.AmountsOwed(matchedLendersOwed, matchedBorrowersOwed, matchedCollateralLendersOwed);
// Clear Ajna deposits and burn tokens
_clearDepositsAndBurnTokens(pool);
// Process matches and create new orders
_processMatchesAndCreateOrders(matchedLendersOwed, matchedBorrowersOwed, matchedCollateralLendersOwed);
// Final cleanup
LVLidoVault.end_epoch();
LVLidoVault.setAllowKick(false);
}
/**
* @notice Processes Lido withdrawal claims
* @param t1Debt Current debt amount
* @return claimAmount Amount claimed from Lido
*/
function _processLidoWithdrawal(uint256 t1Debt) internal returns (uint256 claimAmount) {
uint256 firstIndex = 1;
uint256 lastIndex = VaultLib.LIDO_WITHDRAWAL.getLastCheckpointIndex();
uint256[] memory requestIds = new uint256[](1);
requestIds[0] = LVLidoVault.requestId();
uint256[] memory hints = VaultLib.LIDO_WITHDRAWAL.findCheckpointHints(requestIds, firstIndex, lastIndex);
uint256[] memory claimableEthValues = VaultLib.LIDO_WITHDRAWAL.getClaimableEther(requestIds, hints);
claimAmount = claimableEthValues[0];
if (claimAmount > 0) {
LVLidoVault.claimWithdrawal();
emit VaultLib.FundsClaimed(LVLidoVault.requestId(), claimAmount);
LVLidoVault.depositEthForWeth(claimAmount);
} else {
if (t1Debt != 0) {
revert VaultLib.NoETHToClaim();
}
}
}
/**
* @notice Processes debt repayment and calculates lenders owed
* @param t1Debt Current t1 debt
* @param actualDebt Calculated actual debt with interest
* @param claimAmount Amount claimed from Lido
* @param pool The Ajna pool
* @return matchedLendersOwed Amount owed to matched lenders
*/
function _processDebtAndCalculateOwed(
uint256 t1Debt,
uint256 actualDebt,
uint256 claimAmount,
IERC20Pool pool
) internal returns (uint256 matchedLendersOwed) {
if (actualDebt > claimAmount + LVLidoVault.totalManualRepay()) {
revert DebtGreaterThanAvailableFunds();
} else {
if (actualDebt < claimAmount) {
LVLidoVault.wethToWsteth(claimAmount - actualDebt);
} else {
LVLidoVault.setTotalManualRepay(LVLidoVault.totalManualRepay() - (actualDebt - claimAmount));
}
}
if (actualDebt > 0) {
bool mintSuccess = LVLidoVault.mintForProxy(address(LVLidoVault.testQuoteToken()), address(LVLidoVault), t1Debt);
bool approveSuccess = LVLidoVault.approveForProxy(address(LVLidoVault.testQuoteToken()), address(pool), t1Debt);
if (!mintSuccess || !approveSuccess) revert UpkeepFailed();
matchedLendersOwed = LVLidoVault.totalLenderQTUtilized() - LVLidoVault.totalBorrowAmount() + actualDebt;
} else {
matchedLendersOwed = IERC20(VaultLib.QUOTE_TOKEN).balanceOf(address(LVLidoVault))
- LVLidoVault.totalLenderQTUnutilized();
}
}
/**
* @notice Calculates amount owed to collateral lenders (0.14% APY)
* @param timeElapsed Time elapsed since epoch start
* @return matchedCollateralLendersOwed Amount owed to collateral lenders
*/
function _calculateCollateralLendersOwed(uint256 timeElapsed) internal view returns (uint256 matchedCollateralLendersOwed) {
matchedCollateralLendersOwed = (
(LVLidoVault.totalCLDepositsUnutilized() + LVLidoVault.totalCLDepositsUtilized())
* (1e18 + ((timeElapsed * 14e14) / 365 days))
) / 1e18;
uint256 totalEpochCollateral = IERC20(VaultLib.COLLATERAL_TOKEN).balanceOf(address(LVLidoVault))
- LVLidoVault.totalBorrowerCTUnutilized() - LVLidoVault.totalCollateralLenderCT();
if (matchedCollateralLendersOwed > totalEpochCollateral) {
matchedCollateralLendersOwed = totalEpochCollateral;
}
}
/**
* @notice Calculates amount owed to borrowers
* @param matchedCollateralLendersOwed Amount already allocated to collateral lenders
* @return matchedBorrowersOwed Remaining collateral owed to borrowers
*/
function _calculateBorrowersOwed(uint256 matchedCollateralLendersOwed) internal view returns (uint256 matchedBorrowersOwed) {
uint256 totalEpochCollateral = IERC20(VaultLib.COLLATERAL_TOKEN).balanceOf(address(LVLidoVault))
- LVLidoVault.totalBorrowerCTUnutilized() - LVLidoVault.totalCollateralLenderCT();
if (matchedCollateralLendersOwed > totalEpochCollateral) {
matchedBorrowersOwed = 0;
} else {
matchedBorrowersOwed = totalEpochCollateral - matchedCollateralLendersOwed;
}
}
/**
* @notice Clears Ajna deposits and burns tokens
* @param pool The Ajna pool
*/
function _clearDepositsAndBurnTokens(IERC20Pool pool) internal {
uint256 depositSize = pool.depositSize();
LVLidoVault.clearAjnaDeposits(depositSize);
bool burnCTSuccess = LVLidoVault.burnForProxy(
address(LVLidoVault.testCollateralToken()),
address(LVLidoVault),
LVLidoVault.testCollateralToken().balanceOf(address(LVLidoVault))
);
bool burnQTSuccess = LVLidoVault.burnForProxy(
address(LVLidoVault.testQuoteToken()),
address(LVLidoVault),
IERC20(address(LVLidoVault.testQuoteToken())).balanceOf(address(LVLidoVault))
);
if (!burnCTSuccess || !burnQTSuccess) revert UpkeepFailed();
}
/**
* @notice Processes epoch matches and creates new orders for participants
* @param matchedLendersOwed Amount owed to lenders
* @param matchedBorrowersOwed Amount owed to borrowers
* @param matchedCollateralLendersOwed Amount owed to collateral lenders
*/
function _processMatchesAndCreateOrders(
uint256 matchedLendersOwed,
uint256 matchedBorrowersOwed,
uint256 matchedCollateralLendersOwed
) internal {
uint256 totalLenderQTUnutilizedToAdjust;
uint256 newTotalBorrowerCT = LVLidoVault.totalBorrowerCT();
uint256 newTotalBorrowerCTUnutilized = LVLidoVault.totalBorrowerCTUnutilized();
uint256 currentEpoch = LVLidoVault.epoch();
// Process lender and borrower matches
VaultLib.MatchInfo[] memory matches = LVLidoVault.getEpochMatches(currentEpoch);
for (uint256 i = 0; i < matches.length; i++) {
VaultLib.MatchInfo memory match_ = matches[i];
uint256 newLenderQuoteAmount = (
(match_.quoteAmount + match_.reservedQuoteAmount) * matchedLendersOwed
) / LVLidoVault.totalLenderQTUtilized();
uint256 newBorrowerCTAmount = (match_.collateralAmount * matchedBorrowersOwed)
/ (LVLidoVault.totalBorrowerCT() - LVLidoVault.totalBorrowerCTUnutilized());
LVLidoVault.lenderOrdersPush(VaultLib.LenderOrder(match_.lender, newLenderQuoteAmount, 0));
LVLidoVault.borrowerOrdersPush(VaultLib.BorrowerOrder(match_.borrower, newBorrowerCTAmount));
totalLenderQTUnutilizedToAdjust += newLenderQuoteAmount;
newTotalBorrowerCTUnutilized += newBorrowerCTAmount;
newTotalBorrowerCT = newTotalBorrowerCT - match_.collateralAmount + newBorrowerCTAmount;
}
// Emit interest earned event
emit VaultLib.EpochInterestEarned(
currentEpoch,
(LVLidoVault.totalLenderQTUnutilized() + totalLenderQTUnutilizedToAdjust) > LVLidoVault.totalLenderQTUtilized()
? (LVLidoVault.totalLenderQTUnutilized() + totalLenderQTUnutilizedToAdjust) - LVLidoVault.totalLenderQTUtilized()
: 0,
newTotalBorrowerCT > LVLidoVault.totalBorrowerCT()
? newTotalBorrowerCT - LVLidoVault.totalBorrowerCT()
: 0,
matchedCollateralLendersOwed > (LVLidoVault.totalCLDepositsUnutilized() + LVLidoVault.totalCLDepositsUtilized())
? matchedCollateralLendersOwed - (LVLidoVault.totalCLDepositsUnutilized() + LVLidoVault.totalCLDepositsUtilized())
: 0
);
// Update lender totals
LVLidoVault.setTotalLenderQTUnutilized(LVLidoVault.totalLenderQTUnutilized() + totalLenderQTUnutilizedToAdjust);
LVLidoVault.setTotalLenderQTUtilized(0);
// Update borrower totals
LVLidoVault.setTotalBorrowerCT(newTotalBorrowerCT);
LVLidoVault.setTotalBorrowerCTUnutilized(newTotalBorrowerCTUnutilized);
// Delete epoch matches
LVLidoVault.deleteEpochMatches(currentEpoch);
// Process collateral lender orders
VaultLib.CollateralLenderOrder[] memory clOrders = LVLidoVault.getEpochCollateralLenderOrders(currentEpoch);
uint256 totalCLDeposits = LVLidoVault.totalCLDepositsUnutilized() + LVLidoVault.totalCLDepositsUtilized();
for (uint256 i = 0; i < clOrders.length; i++) {
VaultLib.CollateralLenderOrder memory clOrder = clOrders[i];
uint256 newCLCollateralAmount = (clOrder.collateralAmount * matchedCollateralLendersOwed) / totalCLDeposits;
LVLidoVault.collateralLenderOrdersPush(
VaultLib.CollateralLenderOrder(clOrder.collateralLender, newCLCollateralAmount)
);
LVLidoVault.setTotalCollateralLenderCT(LVLidoVault.totalCollateralLenderCT() + newCLCollateralAmount);
}
// Cleanup collateral lender epoch data
LVLidoVault.deleteEpochCollateralLenderOrders(currentEpoch);
LVLidoVault.setTotalCLDepositsUnutilized(0);
LVLidoVault.setTotalCLDepositsUtilized(0);
}
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity ^0.8.20;
interface IWsteth {
/**
* @notice Returns the amount of stETH that corresponds to one wstETH.
* @dev The value increases over time as staking rewards accumulate.
* @return The amount of stETH per one wstETH, scaled to 18 decimals.
*/
function stEthPerToken() external view returns (uint256);
/**
* @notice Returns the amount of wstETH that corresponds to one stETH.
* @dev This value decreases over time as staking rewards accumulate.
* @return The amount of wstETH per one stETH, scaled to 18 decimals.
*/
function tokensPerStEth() external view returns (uint256);
/**
* @notice Wraps stETH into wstETH.
* @dev Transfers `stETH` from the caller and mints `wstETH` to the caller.
* @param _stETHAmount The amount of stETH to wrap.
* @return The amount of wstETH minted.
*/
function wrap(uint256 _stETHAmount) external returns (uint256);
/**
* @notice Returns the balance of the account.
* @param account The address of the account.
* @return The balance of the account.
*/
function balanceOf(address account) external view returns (uint256);
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity ^0.8.20;
import {ILidoWithdrawal} from "../interfaces/vault/ILidoWithdrawal.sol";
/**
* @title VaultLib
*/
library VaultLib {
struct LenderOrder {
address lender;
uint256 quoteAmount;
uint256 vaultShares;
}
struct BorrowerOrder {
address borrower;
uint256 collateralAmount;
}
struct CollateralLenderOrder {
address collateralLender;
uint256 collateralAmount;
}
struct MatchInfo {
address lender;
address borrower;
uint256 quoteAmount; // 97%
uint256 collateralAmount;
uint256 reservedQuoteAmount; // 3%
}
address public constant QUOTE_TOKEN = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address public constant COLLATERAL_TOKEN = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;
address public constant STETH_ADDRESS = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;
ILidoWithdrawal public constant LIDO_WITHDRAWAL = ILidoWithdrawal(0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1);
address public constant router = 0x65Dcc24F8ff9e51F10DCc7Ed1e4e2A61e6E14bd6;
bytes32 public constant donId = 0x66756e2d657468657265756d2d6d61696e6e65742d3100000000000000000000;
uint256 public constant leverageFactor = 80;
/* EVENTS */
event EndEpoch(uint256 time);
event TermEnded(uint256 time);
event FundsQueued(uint256 requestId, uint256 collateralAmount);
event FundsClaimed(uint256 requestId, uint256 amount);
event FundsAdded(uint256 ethAmount, uint256 contractBalance, address sender);
event ForwarderAddressUpdated(address oldAddress, address newAddress);
event AvoidLiquidation(uint256 collateralAmount);
event EpochStarted(uint256 epoch, uint256 epochStart, uint256 termEnd);
event RedemptionRateUpdated(uint256 redemptionRate);
event OCRResponse(bytes32 indexed requestId, bytes response, uint256 rate, bytes err);
event RequestRevertedWithErrorMsg(string reason);
event RequestRevertedWithoutErrorMsg(bytes data);
event LVLidoVaultUtilAddressUpdated(address oldAddress, address newAddress);
event LoanComposition(
uint256 baseCollateral, uint256 leveragedCollateral, uint256 totalCollateral, uint256 quoteToBorrow
);
event AmountsOwed(uint256 lendersOwed, uint256 borrowersOwed, uint256 collateralLendersOwed);
// ------------------------------------------------------------
// Confirmed to be used
event LenderOrderAdded(address lender, uint256 quoteAmount);
event BorrowerOrderAdded(address borrower, uint256 collateralAmount);
event CollateralLenderDeposit(address collateralLender, uint256 collateralAmount);
event WithdrawLender(address lender, uint256 quoteAmount);
event WithdrawBorrower(address borrower, uint256 collateralAmount);
event WithdrawCollateralLender(address collateralLender, uint256 collateralAmount);
event EpochInterestEarned(
uint256 epochNumber,
uint256 lendersInterestAccrued,
uint256 borrowersInterestAccrued,
uint256 collateralLendersInterestAccrued
);
/* Errors */
error InsufficientFunds();
error Unauthorized();
error OnlyForwarder();
error OnlyProxy();
error ReentrantCall();
error InvalidInput();
error NoETHToClaim();
error NoUnfilledOrdersFound();
error MaxFundsExceeded();
error TokenOperationFailed();
error LockedBonds();
// ------------------------------------------------------------
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity ^0.8.20;
/**
* @title Pool Info Utils contract
* @notice Contract for providing information for any deployed pool.
* @dev Pool info is calculated using same helper functions / logic as in `Pool` contracts.
*/
interface IPoolInfoUtils {
/**
* @notice Exposes status of a liquidation auction.
* @param ajnaPool_ Address of `Ajna` pool.
* @param borrower_ Identifies the loan being liquidated.
* @return kickTime_ Time auction was kicked, implying end time.
* @return collateral_ Remaining collateral available to be purchased. (`WAD`)
* @return debtToCover_ Borrower debt to be covered. (`WAD`)
* @return isCollateralized_ `True` if loan is collateralized.
* @return price_ Current price of the auction. (`WAD`)
* @return neutralPrice_ Price at which bond holder is neither rewarded nor penalized. (`WAD`)
* @return referencePrice_ Price used to determine auction start price. (`WAD`)
* @return debtToCollateral_ Borrower debt to collateral at time of kick. (`WAD`)
* @return bondFactor_ The factor used for calculating bond size. (`WAD`)
*/
function auctionStatus(address ajnaPool_, address borrower_)
external
view
returns (
uint256 kickTime_,
uint256 collateral_,
uint256 debtToCover_,
bool isCollateralized_,
uint256 price_,
uint256 neutralPrice_,
uint256 referencePrice_,
uint256 debtToCollateral_,
uint256 bondFactor_
);
/**
* @notice Returns details of an auction for a given borrower address.
* @dev Calls and returns all values from pool.auctionInfo().
* @param ajnaPool_ Address of `Ajna` pool.
* @param borrower_ Address of the borrower that is liquidated.
* @return kicker_ Address of the kicker that is kicking the auction.
* @return bondFactor_ The factor used for calculating bond size.
* @return bondSize_ The bond amount in quote token terms.
* @return kickTime_ Time the liquidation was initiated.
* @return referencePrice_ Price used to determine auction start price.
* @return neutralPrice_ `Neutral Price` of auction.
* @return debtToCollateral_ Borrower debt to collateral at time of kick, which is used in BPF for kicker's reward calculation.
* @return head_ Address of the head auction.
* @return next_ Address of the next auction in queue.
* @return prev_ Address of the prev auction in queue.
*/
function auctionInfo(address ajnaPool_, address borrower_)
external
view
returns (
address kicker_,
uint256 bondFactor_,
uint256 bondSize_,
uint256 kickTime_,
uint256 referencePrice_,
uint256 neutralPrice_,
uint256 debtToCollateral_,
address head_,
address next_,
address prev_
);
/**
* @notice Retrieves info of a given borrower in a given `Ajna` pool.
* @param ajnaPool_ Address of `Ajna` pool.
* @param borrower_ Borrower's address.
* @return debt_ Current debt owed by borrower (`WAD`).
* @return collateral_ Pledged collateral, including encumbered (`WAD`).
* @return t0Np_ `Neutral price` (`WAD`).
* @return thresholdPrice_ Borrower's `Threshold Price` (`WAD`).
*/
function borrowerInfo(address ajnaPool_, address borrower_)
external
view
returns (uint256 debt_, uint256 collateral_, uint256 t0Np_, uint256 thresholdPrice_);
/**
* @notice Get a bucket struct for a given index.
* @param ajnaPool_ Address of `Ajna` pool.
* @param index_ The index of the bucket to retrieve.
* @return price_ Bucket's price (`WAD`).
* @return quoteTokens_ Amount of quote token in bucket, `deposit + interest` (`WAD`).
* @return collateral_ Unencumbered collateral in bucket (`WAD`).
* @return bucketLP_ Outstanding `LP` balance in bucket (`WAD`).
* @return scale_ Lender interest multiplier (`WAD`).
* @return exchangeRate_ The exchange rate of the bucket, in `WAD` units.
*/
function bucketInfo(address ajnaPool_, uint256 index_)
external
view
returns (
uint256 price_,
uint256 quoteTokens_,
uint256 collateral_,
uint256 bucketLP_,
uint256 scale_,
uint256 exchangeRate_
);
/**
* @notice Returns info related to pool loans.
* @param ajnaPool_ Address of `Ajna` pool.
* @return poolSize_ The total amount of quote tokens in pool (`WAD`).
* @return loansCount_ The number of loans in pool.
* @return maxBorrower_ The address with the highest `TP` in pool.
* @return pendingInflator_ Pending inflator in pool.
* @return pendingInterestFactor_ Factor used to scale the inflator.
*/
function poolLoansInfo(address ajnaPool_)
external
view
returns (
uint256 poolSize_,
uint256 loansCount_,
address maxBorrower_,
uint256 pendingInflator_,
uint256 pendingInterestFactor_
);
/**
* @notice Returns info related to pool prices.
* @param ajnaPool_ Address of `Ajna` pool.
* @return hpb_ The price value of the current `Highest Price Bucket` (`HPB`), in `WAD` units.
* @return hpbIndex_ The index of the current `Highest Price Bucket` (`HPB`), in `WAD` units.
* @return htp_ The price value of the current `Highest Threshold Price` (`HTP`) bucket, in `WAD` units.
* @return htpIndex_ The index of the current `Highest Threshold Price` (`HTP`) bucket, in `WAD` units.
* @return lup_ The price value of the current `Lowest Utilized Price` (LUP) bucket, in `WAD` units.
* @return lupIndex_ The index of the current `Lowest Utilized Price` (`LUP`) bucket, in `WAD` units.
*/
function poolPricesInfo(address ajnaPool_)
external
view
returns (uint256 hpb_, uint256 hpbIndex_, uint256 htp_, uint256 htpIndex_, uint256 lup_, uint256 lupIndex_);
/**
* @notice Returns the amount of quote token available for borrowing or removing from pool.
* @dev Calculated as the difference between pool balance and escrowed amounts locked in pool (auction bons + unclaimed reserves).
* @param ajnaPool_ Address of `Ajna` pool.
* @return amount_ The total quote token amount available to borrow or to be removed from pool, in `WAD` units.
*/
function availableQuoteTokenAmount(address ajnaPool_) external view returns (uint256 amount_);
/**
* @notice Returns info related to `Claimaible Reserve Auction`.
* @param ajnaPool_ Address of `Ajna` pool.
* @return reserves_ The amount of excess quote tokens.
* @return claimableReserves_ Denominated in quote token, or `0` if no reserves can be auctioned.
* @return claimableReservesRemaining_ Amount of claimable reserves which has not yet been taken.
* @return auctionPrice_ Current price at which `1` quote token may be purchased, denominated in `Ajna`.
* @return timeRemaining_ Seconds remaining before takes are no longer allowed.
*/
function poolReservesInfo(address ajnaPool_)
external
view
returns (
uint256 reserves_,
uint256 claimableReserves_,
uint256 claimableReservesRemaining_,
uint256 auctionPrice_,
uint256 timeRemaining_
);
/**
* @notice Returns info related to pool utilization.
* @param ajnaPool_ Address of `Ajna` pool.
* @return poolMinDebtAmount_ Minimum debt amount.
* @return poolCollateralization_ Current pool collateralization ratio.
* @return poolActualUtilization_ The current pool actual utilization, in `WAD` units.
* @return poolTargetUtilization_ The current pool Target utilization, in `WAD` units.
*/
function poolUtilizationInfo(address ajnaPool_)
external
view
returns (
uint256 poolMinDebtAmount_,
uint256 poolCollateralization_,
uint256 poolActualUtilization_,
uint256 poolTargetUtilization_
);
/**
* @notice Returns the proportion of interest rate which is awarded to lenders;
* the remainder accumulates in reserves.
* @param ajnaPool_ Address of `Ajna` pool.
* @return lenderInterestMargin_ Lender interest margin in pool.
*/
function lenderInterestMargin(address ajnaPool_) external view returns (uint256 lenderInterestMargin_);
/**
* @notice Returns bucket price for a given bucket index.
*/
function indexToPrice(uint256 index_) external pure returns (uint256);
/**
* @notice Returns bucket index for a given bucket price.
*/
function priceToIndex(uint256 price_) external pure returns (uint256);
/**
* @notice Returns current `LUP` for a given pool.
*/
function lup(address ajnaPool_) external view returns (uint256);
/**
* @notice Returns current `LUP` index for a given pool.
*/
function lupIndex(address ajnaPool_) external view returns (uint256);
/**
* @notice Returns current `HPB` for a given pool.
*/
function hpb(address ajnaPool_) external view returns (uint256);
/**
* @notice Returns current `HPB` index for a given pool.
*/
function hpbIndex(address ajnaPool_) external view returns (uint256);
/**
* @notice Returns current `HTP` for a given pool.
*/
function htp(address ajnaPool_) external view returns (uint256 htp_);
/**
* @notice Calculates origination fee rate for a pool.
* @notice Calculated as greater of the current annualized interest rate divided by `52` (one week of interest) or `5` bps.
* @return Fee rate calculated from the pool interest rate.
*/
function borrowFeeRate(address ajnaPool_) external view returns (uint256);
/**
* @notice Calculates deposit fee rate for a pool.
* @notice Calculated as current annualized rate divided by 365 * 3 (8 hours of interest)
* @return Fee rate calculated from the pool interest rate.
*/
function depositFeeRate(address ajnaPool_) external view returns (uint256);
/**
* @notice Calculate the amount of quote tokens in bucket for a given amount of `LP`.
* @param lp_ The number of `LP` to calculate amounts for.
* @param index_ The price bucket index for which the value should be calculated.
* @return quoteAmount_ The exact amount of quote tokens that can be exchanged for the given `LP`, `WAD` units.
*/
function lpToQuoteTokens(address ajnaPool_, uint256 lp_, uint256 index_)
external
view
returns (uint256 quoteAmount_);
/**
* @notice Calculate the amount of collateral tokens in bucket for a given amount of `LP`.
* @param lp_ The number of `LP` to calculate amounts for.
* @param index_ The price bucket index for which the value should be calculated.
* @return collateralAmount_ The exact amount of collateral tokens that can be exchanged for the given `LP`, `WAD` units.
*/
function lpToCollateral(address ajnaPool_, uint256 lp_, uint256 index_)
external
view
returns (uint256 collateralAmount_);
}
// /**********************/
// /*** Pool Utilities ***/
// /**********************/
// /**
// * @notice Calculates encumberance for a debt amount at a given price.
// * @param debt_ The debt amount to calculate encumberance for.
// * @param price_ The price to calculate encumberance at.
// * @return encumberance_ Encumberance value.
// */
// function _encumberance(
// uint256 debt_,
// uint256 price_
// ) pure returns (uint256 encumberance_) {
// return price_ != 0 ? Maths.wdiv(Maths.wmul(COLLATERALIZATION_FACTOR , debt_), price_) : 0;
// }
// /**
// * @notice Calculates collateralization for a given debt and collateral amounts, at a given price.
// * @param debt_ The debt amount.
// * @param collateral_ The collateral amount.
// * @param price_ The price to calculate collateralization at.
// * @return Collateralization value. `1 WAD` if debt amount is `0`.
// */
// function _collateralization(
// uint256 debt_,
// uint256 collateral_,
// uint256 price_
// ) pure returns (uint256) {
// // cannot be undercollateralized if there is no debt
// if (debt_ == 0) return 1e18;
// // borrower is undercollateralized when lup at MIN_PRICE
// if (price_ == MIN_PRICE) return 0;
// return Maths.wdiv(Maths.wmul(collateral_, price_), Maths.wmul(COLLATERALIZATION_FACTOR, debt_));
// }
// /**
// * @notice Calculates target utilization for given `EMA` values.
// * @param debtColEma_ The `EMA` of debt squared to collateral.
// * @param lupt0DebtEma_ The `EMA` of `LUP * t0 debt`.
// * @return Target utilization of the pool.
// */
// function _targetUtilization(
// uint256 debtColEma_,
// uint256 lupt0DebtEma_
// ) pure returns (uint256) {
// return (lupt0DebtEma_ != 0) ? Maths.wdiv(debtColEma_, lupt0DebtEma_) : Maths.WAD;
// }// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
import {IPool} from "../IPool.sol";
import {IERC20PoolBorrowerActions} from "./IERC20PoolBorrowerActions.sol";
import {IERC20PoolLenderActions} from "./IERC20PoolLenderActions.sol";
import {IERC20PoolImmutables} from "./IERC20PoolImmutables.sol";
import {IERC20PoolEvents} from "./IERC20PoolEvents.sol";
/**
* @title ERC20 Pool
*/
interface IERC20Pool is
IPool,
IERC20PoolLenderActions,
IERC20PoolBorrowerActions,
IERC20PoolImmutables,
IERC20PoolEvents
{
/**
* @notice Initializes a new pool, setting initial state variables.
* @param rate_ Initial interest rate of the pool (min accepted value 1%, max accepted value 10%).
*/
function initialize(uint256 rate_) external;
/**
* @notice Returns the minimum amount of collateral an actor may have in a bucket.
* @param bucketIndex_ The bucket index for which the dust limit is desired, or `0` for pledged collateral.
* @return The dust limit for `bucketIndex_`.
*/
function bucketCollateralDust(uint256 bucketIndex_) external pure returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity ^0.8.20;
interface IWeth {
function withdraw(uint256 amount) external;
function deposit() external payable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity ^0.8.20;
interface ILidoWithdrawal {
// @notice Handles withdrawal request finalization and ETH locking. Requests can be nominal (1:1) or discounted based on share rate.
struct BatchesCalculationState {
uint256 remainingEthBudget;
bool finished;
uint256[36] batches;
uint256 batchesLength;
}
function requestWithdrawalsWstETH(uint256[] calldata _amounts, address _owner)
external
returns (uint256[] memory requestIds);
function claimWithdrawal(uint256 _requestId) external;
function getClaimableEther(uint256[] calldata _requestIds, uint256[] calldata _hints)
external
view
returns (uint256[] memory claimableEthValues);
function getLastCheckpointIndex() external view returns (uint256);
function findCheckpointHints(uint256[] calldata _requestIds, uint256 _firstIndex, uint256 _lastIndex)
external
view
returns (uint256[] memory hintIds);
function finalize(uint256 _lastRequestIdToBeFinalized, uint256 _maxShareRate) external payable;
function prefinalize(uint256[] calldata _batches, uint256 _maxShareRate)
external
view
returns (uint256 ethToLock, uint256 sharesToBurn);
function calculateFinalizationBatches(
uint256 _maxShareRate,
uint256 _maxTimestamp,
uint256 _maxRequestsPerCall,
BatchesCalculationState memory _state
) external view returns (BatchesCalculationState memory);
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
import {IPoolBorrowerActions} from "./commons/IPoolBorrowerActions.sol";
import {IPoolLPActions} from "./commons/IPoolLPActions.sol";
import {IPoolLenderActions} from "./commons/IPoolLenderActions.sol";
import {IPoolKickerActions} from "./commons/IPoolKickerActions.sol";
import {IPoolTakerActions} from "./commons/IPoolTakerActions.sol";
import {IPoolSettlerActions} from "./commons/IPoolSettlerActions.sol";
import {IPoolImmutables} from "./commons/IPoolImmutables.sol";
import {IPoolState} from "./commons/IPoolState.sol";
import {IPoolDerivedState} from "./commons/IPoolDerivedState.sol";
import {IPoolEvents} from "./commons/IPoolEvents.sol";
import {IPoolErrors} from "./commons/IPoolErrors.sol";
import {IERC3156FlashLender} from "./IERC3156FlashLender.sol";
/**
* @title Base Pool Interface
*/
interface IPool is
IPoolBorrowerActions,
IPoolLPActions,
IPoolLenderActions,
IPoolKickerActions,
IPoolTakerActions,
IPoolSettlerActions,
IPoolImmutables,
IPoolState,
IPoolDerivedState,
IPoolEvents,
IPoolErrors,
IERC3156FlashLender
{}
/// @dev Pool type enum - `ERC20` and `ERC721`
enum PoolType {
ERC20,
ERC721
}
/// @dev `ERC20` token interface.
interface IERC20Token {
function balanceOf(address account) external view returns (uint256);
function burn(uint256 amount) external;
function decimals() external view returns (uint8);
}
/// @dev `ERC721` token interface.
interface IERC721Token {
function transferFrom(address from, address to, uint256 tokenId) external;
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
/**
* @title ERC20 Pool Borrower Actions
*/
interface IERC20PoolBorrowerActions {
/**
* @notice Called by borrowers to add collateral to the pool and/or borrow quote from the pool.
* @dev Can be called by borrowers with either `0` `amountToBorrow_` or `0` `collateralToPledge_`, if borrower only wants to take a single action.
* @param borrowerAddress_ The borrower to whom collateral was pledged, and/or debt was drawn for.
* @param amountToBorrow_ The amount of quote tokens to borrow (`WAD` precision).
* @param limitIndex_ Lower bound of `LUP` change (if any) that the borrower will tolerate from a creating or modifying position.
* @param collateralToPledge_ The amount of collateral to be added to the pool (`WAD` precision).
*/
function drawDebt(
address borrowerAddress_,
uint256 amountToBorrow_,
uint256 limitIndex_,
uint256 collateralToPledge_
) external;
/**
* @notice Called by borrowers to repay borrowed quote to the pool, and/or pull collateral form the pool.
* @dev Can be called by borrowers with either `0` `maxQuoteTokenAmountToRepay_` or `0` `collateralAmountToPull_`, if borrower only wants to take a single action.
* @param borrowerAddress_ The borrower whose loan is being interacted with.
* @param maxQuoteTokenAmountToRepay_ The max amount of quote tokens to repay (`WAD` precision).
* @param collateralAmountToPull_ The max amount of collateral to be puled from the pool (`WAD` precision).
* @param recipient_ The address to receive amount of pulled collateral.
* @param limitIndex_ Ensures `LUP` has not moved far from state when borrower pulls collateral.
* @return amountRepaid_ The amount of quote token repaid (`WAD` precision).
*/
function repayDebt(
address borrowerAddress_,
uint256 maxQuoteTokenAmountToRepay_,
uint256 collateralAmountToPull_,
address recipient_,
uint256 limitIndex_
) external returns (uint256 amountRepaid_);
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
/**
* @title ERC20 Pool Lender Actions
*/
interface IERC20PoolLenderActions {
/**
* @notice Deposit claimable collateral into a specified bucket.
* @param amountToAdd_ Amount of collateral to deposit (`WAD` precision).
* @param index_ The bucket index to which collateral will be deposited.
* @param expiry_ Timestamp after which this transaction will revert, preventing inclusion in a block with unfavorable price.
* @return bucketLP_ The amount of `LP` awarded for the added collateral (`WAD` precision).
*/
function addCollateral(uint256 amountToAdd_, uint256 index_, uint256 expiry_)
external
returns (uint256 bucketLP_);
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
/**
* @title ERC20 Pool Immutables
*/
interface IERC20PoolImmutables {
/**
* @notice Returns the `collateralScale` immutable.
* @return The precision of the collateral `ERC20` token based on decimals.
*/
function collateralScale() external view returns (uint256);
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
/**
* @title ERC20 Pool Events
*/
interface IERC20PoolEvents {
/**
* @notice Emitted when actor adds claimable collateral to a bucket.
* @param actor Recipient that added collateral.
* @param index Index at which collateral were added.
* @param amount Amount of collateral added to the pool (`WAD` precision).
* @param lpAwarded Amount of `LP` awarded for the deposit (`WAD` precision).
*/
event AddCollateral(address indexed actor, uint256 indexed index, uint256 amount, uint256 lpAwarded);
/**
* @notice Emitted when borrower draws debt from the pool, or adds collateral to the pool.
* @param borrower The borrower to whom collateral was pledged, and/or debt was drawn for.
* @param amountBorrowed Amount of quote tokens borrowed from the pool (`WAD` precision).
* @param collateralPledged Amount of collateral locked in the pool (`WAD` precision).
* @param lup `LUP` after borrow.
*/
event DrawDebt(address indexed borrower, uint256 amountBorrowed, uint256 collateralPledged, uint256 lup);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
/**
* @title Pool Borrower Actions
*/
interface IPoolBorrowerActions {
/**
* @notice Called by fully collateralized borrowers to restamp the `Np to Tp ratio` of the loan (only if loan is fully collateralized and not in auction).
* The reason for stamping the `Np to Tp ratio` on the loan is to provide some certainty to the borrower as to at what price they can expect to be liquidated.
* This action can restamp only the loan of `msg.sender`.
*/
function stampLoan() external;
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
/**
* @title Pool `LP` Actions
*/
interface IPoolLPActions {
/**
* @notice Called by `LP` owners to approve transfer of an amount of `LP` to a new owner.
* @dev Intended for use by the `PositionManager` contract.
* @param spender_ The new owner of the `LP`.
* @param indexes_ Bucket indexes from where `LP` are transferred.
* @param amounts_ The amounts of `LP` approved to transfer (`WAD` precision).
*/
function increaseLPAllowance(address spender_, uint256[] calldata indexes_, uint256[] calldata amounts_) external;
/**
* @notice Called by `LP` owners to decrease the amount of `LP` that can be spend by a new owner.
* @dev Intended for use by the `PositionManager` contract.
* @param spender_ The new owner of the `LP`.
* @param indexes_ Bucket indexes from where `LP` are transferred.
* @param amounts_ The amounts of `LP` disapproved to transfer (`WAD` precision).
*/
function decreaseLPAllowance(address spender_, uint256[] calldata indexes_, uint256[] calldata amounts_) external;
/**
* @notice Called by `LP` owners to decrease the amount of `LP` that can be spend by a new owner.
* @param spender_ Address that is having it's allowance revoked.
* @param indexes_ List of bucket index to remove the allowance from.
*/
function revokeLPAllowance(address spender_, uint256[] calldata indexes_) external;
/**
* @notice Called by `LP` owners to allow addresses that can transfer LP.
* @dev Intended for use by the `PositionManager` contract.
* @param transferors_ Addresses that are allowed to transfer `LP` to new owner.
*/
function approveLPTransferors(address[] calldata transferors_) external;
/**
* @notice Called by `LP` owners to revoke addresses that can transfer `LP`.
* @dev Intended for use by the `PositionManager` contract.
* @param transferors_ Addresses that are revoked to transfer `LP` to new owner.
*/
function revokeLPTransferors(address[] calldata transferors_) external;
/**
* @notice Called by `LP` owners to transfers their `LP` to a different address. `approveLpOwnership` needs to be run first.
* @dev Used by `PositionManager.memorializePositions()`.
* @param owner_ The original owner address of the position.
* @param newOwner_ The new owner address of the position.
* @param indexes_ Array of price buckets index at which `LP` were moved.
*/
function transferLP(address owner_, address newOwner_, uint256[] calldata indexes_) external;
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
/**
* @title Pool Lender Actions
*/
interface IPoolLenderActions {
/**
*
*/
/**
* Quote/collateral management functions **
*/
/**
*
*/
/**
* @notice Called by lenders to add an amount of credit at a specified price bucket.
* @param amount_ The amount of quote token to be added by a lender (`WAD` precision).
* @param index_ The index of the bucket to which the quote tokens will be added.
* @param expiry_ Timestamp after which this transaction will revert, preventing inclusion in a block with unfavorable price.
* @return bucketLP_ The amount of `LP` changed for the added quote tokens (`WAD` precision).
* @return addedAmount_ The amount of quote token added (`WAD` precision).
*/
function addQuoteToken(uint256 amount_, uint256 index_, uint256 expiry_)
external
returns (uint256 bucketLP_, uint256 addedAmount_);
/**
* @notice Called by lenders to move an amount of credit from a specified price bucket to another specified price bucket.
* @param maxAmount_ The maximum amount of quote token to be moved by a lender (`WAD` precision).
* @param fromIndex_ The bucket index from which the quote tokens will be removed.
* @param toIndex_ The bucket index to which the quote tokens will be added.
* @param expiry_ Timestamp after which this transaction will revert, preventing inclusion in a block with unfavorable price.
* @return fromBucketLP_ The amount of `LP` moved out from bucket (`WAD` precision).
* @return toBucketLP_ The amount of `LP` moved to destination bucket (`WAD` precision).
* @return movedAmount_ The amount of quote token moved (`WAD` precision).
*/
function moveQuoteToken(uint256 maxAmount_, uint256 fromIndex_, uint256 toIndex_, uint256 expiry_)
external
returns (uint256 fromBucketLP_, uint256 toBucketLP_, uint256 movedAmount_);
/**
* @notice Called by lenders to claim collateral from a price bucket.
* @param maxAmount_ The amount of collateral (`WAD` precision for `ERC20` pools, number of `NFT` tokens for `ERC721` pools) to claim.
* @param index_ The bucket index from which collateral will be removed.
* @return removedAmount_ The amount of collateral removed (`WAD` precision).
* @return redeemedLP_ The amount of `LP` used for removing collateral amount (`WAD` precision).
*/
function removeCollateral(uint256 maxAmount_, uint256 index_)
external
returns (uint256 removedAmount_, uint256 redeemedLP_);
/**
* @notice Called by lenders to remove an amount of credit at a specified price bucket.
* @param maxAmount_ The max amount of quote token to be removed by a lender (`WAD` precision).
* @param index_ The bucket index from which quote tokens will be removed.
* @return removedAmount_ The amount of quote token removed (`WAD` precision).
* @return redeemedLP_ The amount of `LP` used for removing quote tokens amount (`WAD` precision).
*/
function removeQuoteToken(uint256 maxAmount_, uint256 index_)
external
returns (uint256 removedAmount_, uint256 redeemedLP_);
/**
*
*/
/**
* Interest update function **
*/
/**
*
*/
/**
* @notice Called by actors to update pool interest rate (can be updated only once in a `12` hours period of time).
*/
function updateInterest() external;
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
/**
* @title Pool Kicker Actions
*/
interface IPoolKickerActions {
/**
*
*/
/**
* Liquidations **
*/
/**
*
*/
/**
* @notice Called by actors to initiate a liquidation.
* @param borrower_ Identifies the loan to liquidate.
* @param npLimitIndex_ Index of the lower bound of `NP` tolerated when kicking the auction.
*/
function kick(address borrower_, uint256 npLimitIndex_) external;
/**
* @notice Called by lenders to liquidate the top loan.
* @param index_ The deposit index to use for kicking the top loan.
* @param npLimitIndex_ Index of the lower bound of `NP` tolerated when kicking the auction.
*/
function lenderKick(uint256 index_, uint256 npLimitIndex_) external;
/**
* @notice Called by kickers to withdraw their auction bonds (the amount of quote tokens that are not locked in active auctions).
* @param recipient_ Address to receive claimed bonds amount.
* @param maxAmount_ The max amount to withdraw from auction bonds (`WAD` precision). Constrained by claimable amounts and liquidity.
* @return withdrawnAmount_ The amount withdrawn (`WAD` precision).
*/
function withdrawBonds(address recipient_, uint256 maxAmount_) external returns (uint256 withdrawnAmount_);
/**
*
*/
/**
* Reserve Auction **
*/
/**
*
*/
/**
* @notice Called by actor to start a `Claimable Reserve Auction` (`CRA`).
*/
function kickReserveAuction() external;
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
/**
* @title Pool Taker Actions
*/
interface IPoolTakerActions {
/**
* @notice Called by actors to use quote token to arb higher-priced deposit off the book.
* @param borrowerAddress_ Address of the borower take is being called upon.
* @param depositTake_ If `true` then the take will happen at an auction price equal with bucket price. Auction price is used otherwise.
* @param index_ Index of a bucket, likely the `HPB`, in which collateral will be deposited.
*/
function bucketTake(address borrowerAddress_, bool depositTake_, uint256 index_) external;
/**
* @notice Called by actors to purchase collateral from the auction in exchange for quote token.
* @param borrowerAddress_ Address of the borower take is being called upon.
* @param maxAmount_ Max amount of collateral that will be taken from the auction (`WAD` precision for `ERC20` pools, max number of `NFT`s for `ERC721` pools).
* @param callee_ Identifies where collateral should be sent and where quote token should be obtained.
* @param data_ If provided, take will assume the callee implements `IERC*Taker`. Take will send collateral to
* callee before passing this data to `IERC*Taker.atomicSwapCallback`. If not provided,
* the callback function will not be invoked.
* @return collateralTaken_ Amount of collateral taken from the auction (`WAD` precision for `ERC20` pools, max number of `NFT`s for `ERC721` pools).
*/
function take(address borrowerAddress_, uint256 maxAmount_, address callee_, bytes calldata data_)
external
returns (uint256 collateralTaken_);
/**
*
*/
/**
* Reserve Auction **
*/
/**
*
*/
/**
* @notice Purchases claimable reserves during a `CRA` using `Ajna` token.
* @param maxAmount_ Maximum amount of quote token to purchase at the current auction price (`WAD` precision).
* @return amount_ Actual amount of reserves taken (`WAD` precision).
*/
function takeReserves(uint256 maxAmount_) external returns (uint256 amount_);
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
/**
* @title Pool Settler Actions
*/
interface IPoolSettlerActions {
/**
* @notice Called by actors to settle an amount of debt in a completed liquidation.
* @param borrowerAddress_ Address of the auctioned borrower.
* @param maxDepth_ Measured from `HPB`, maximum number of buckets deep to settle debt.
* @return collateralSettled_ Amount of collateral settled.
* @return isBorrowerSettled_ True if all borrower's debt is settled.
* @dev `maxDepth_` is used to prevent unbounded iteration clearing large liquidations.
*/
function settle(address borrowerAddress_, uint256 maxDepth_)
external
returns (uint256 collateralSettled_, bool isBorrowerSettled_);
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
/**
* @title Pool Immutables
*/
interface IPoolImmutables {
/**
* @notice Returns the type of the pool (`0` for `ERC20`, `1` for `ERC721`).
*/
function poolType() external pure returns (uint8);
/**
* @notice Returns the address of the pool's collateral token.
*/
function collateralAddress() external pure returns (address);
/**
* @notice Returns the address of the pool's quote token.
*/
function quoteTokenAddress() external pure returns (address);
/**
* @notice Returns the `quoteTokenScale` state variable.
* @notice Token scale is also the minimum amount a lender may have in a bucket (dust amount).
* @return The precision of the quote `ERC20` token based on decimals.
*/
function quoteTokenScale() external pure returns (uint256);
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
/**
* @title Pool State
*/
interface IPoolState {
/**
* @notice Returns details of an auction for a given borrower address.
* @param borrower_ Address of the borrower that is liquidated.
* @return kicker_ Address of the kicker that is kicking the auction.
* @return bondFactor_ The factor used for calculating bond size.
* @return bondSize_ The bond amount in quote token terms.
* @return kickTime_ Time the liquidation was initiated.
* @return referencePrice_ Price used to determine auction start price.
* @return neutralPrice_ `Neutral Price` of auction.
* @return debtToCollateral_ Borrower debt to collateral, which is used in BPF for kicker's reward calculation.
* @return head_ Address of the head auction.
* @return next_ Address of the next auction in queue.
* @return prev_ Address of the prev auction in queue.
*/
function auctionInfo(address borrower_)
external
view
returns (
address kicker_,
uint256 bondFactor_,
uint256 bondSize_,
uint256 kickTime_,
uint256 referencePrice_,
uint256 neutralPrice_,
uint256 debtToCollateral_,
address head_,
address next_,
address prev_
);
/**
* @notice Returns pool related debt values.
* @return debt_ Current amount of debt owed by borrowers in pool.
* @return accruedDebt_ Debt owed by borrowers based on last inflator snapshot.
* @return debtInAuction_ Total amount of debt in auction.
* @return t0Debt2ToCollateral_ t0debt accross all borrowers divided by their collateral, used in determining a collateralization weighted debt.
*/
function debtInfo()
external
view
returns (uint256 debt_, uint256 accruedDebt_, uint256 debtInAuction_, uint256 t0Debt2ToCollateral_);
/**
* @notice Mapping of borrower addresses to `Borrower` structs.
* @dev NOTE: Cannot use appended underscore syntax for return params since struct is used.
* @param borrower_ Address of the borrower.
* @return t0Debt_ Amount of debt borrower would have had if their loan was the first debt drawn from the pool.
* @return collateral_ Amount of collateral that the borrower has deposited, in collateral token.
* @return npTpRatio_ Np to Tp ratio of borrower at the time of last borrow or pull collateral.
*/
function borrowerInfo(address borrower_)
external
view
returns (uint256 t0Debt_, uint256 collateral_, uint256 npTpRatio_);
/**
* @notice Mapping of buckets indexes to `Bucket` structs.
* @dev NOTE: Cannot use appended underscore syntax for return params since struct is used.
* @param index_ Bucket index.
* @return lpAccumulator_ Amount of `LP` accumulated in current bucket.
* @return availableCollateral_ Amount of collateral available in current bucket.
* @return bankruptcyTime_ Timestamp when bucket become insolvent, `0` if healthy.
* @return bucketDeposit_ Amount of quote tokens in bucket.
* @return bucketScale_ Bucket multiplier.
*/
function bucketInfo(uint256 index_)
external
view
returns (
uint256 lpAccumulator_,
uint256 availableCollateral_,
uint256 bankruptcyTime_,
uint256 bucketDeposit_,
uint256 bucketScale_
);
/**
* @notice Mapping of burnEventEpoch to `BurnEvent` structs.
* @dev Reserve auctions correspond to burn events.
* @param burnEventEpoch_ Id of the current reserve auction.
* @return burnBlock_ Block in which a reserve auction started.
* @return totalInterest_ Total interest as of the reserve auction.
* @return totalBurned_ Total ajna tokens burned as of the reserve auction.
*/
function burnInfo(uint256 burnEventEpoch_) external view returns (uint256, uint256, uint256);
/**
* @notice Returns the latest `burnEventEpoch` of reserve auctions.
* @dev If a reserve auction is active, it refers to the current reserve auction. If no reserve auction is active, it refers to the last reserve auction.
* @return Current `burnEventEpoch`.
*/
function currentBurnEpoch() external view returns (uint256);
/**
* @notice Returns information about the pool `EMA (Exponential Moving Average)` variables.
* @return debtColEma_ Debt squared to collateral Exponential, numerator to `TU` calculation.
* @return lupt0DebtEma_ Exponential of `LUP * t0 debt`, denominator to `TU` calculation
* @return debtEma_ Exponential debt moving average.
* @return depositEma_ sample of meaningful deposit Exponential, denominator to `MAU` calculation.
*/
function emasInfo()
external
view
returns (uint256 debtColEma_, uint256 lupt0DebtEma_, uint256 debtEma_, uint256 depositEma_);
/**
* @notice Returns information about pool inflator.
* @return inflator_ Pool inflator value.
* @return lastUpdate_ The timestamp of the last `inflator` update.
*/
function inflatorInfo() external view returns (uint256 inflator_, uint256 lastUpdate_);
/**
* @notice Returns information about pool interest rate.
* @return interestRate_ Current interest rate in pool.
* @return interestRateUpdate_ The timestamp of the last interest rate update.
*/
function interestRateInfo() external view returns (uint256 interestRate_, uint256 interestRateUpdate_);
/**
* @notice Returns details about kicker balances.
* @param kicker_ The address of the kicker to retrieved info for.
* @return claimable_ Amount of quote token kicker can claim / withdraw from pool at any time.
* @return locked_ Amount of quote token kicker locked in auctions (as bonds).
*/
function kickerInfo(address kicker_) external view returns (uint256 claimable_, uint256 locked_);
/**
* @notice Mapping of buckets indexes and owner addresses to `Lender` structs.
* @param index_ Bucket index.
* @param lender_ Address of the liquidity provider.
* @return lpBalance_ Amount of `LP` owner has in current bucket.
* @return depositTime_ Time the user last deposited quote token.
*/
function lenderInfo(uint256 index_, address lender_)
external
view
returns (uint256 lpBalance_, uint256 depositTime_);
/**
* @notice Return the `LP` allowance a `LP` owner provided to a spender.
* @param index_ Bucket index.
* @param spender_ Address of the `LP` spender.
* @param owner_ The initial owner of the `LP`.
* @return allowance_ Amount of `LP` spender can utilize.
*/
function lpAllowance(uint256 index_, address spender_, address owner_) external view returns (uint256 allowance_);
/**
* @notice Returns information about a loan in the pool.
* @param loanId_ Loan's id within loan heap. Max loan is position `1`.
* @return borrower_ Borrower address at the given position.
* @return t0DebtToCollateral_ Borrower t0 debt to collateral.
*/
function loanInfo(uint256 loanId_) external view returns (address borrower_, uint256 t0DebtToCollateral_);
/**
* @notice Returns information about pool loans.
* @return maxBorrower_ Borrower address with highest t0 debt to collateral.
* @return maxT0DebtToCollateral_ Highest t0 debt to collateral in pool.
* @return noOfLoans_ Total number of loans.
*/
function loansInfo()
external
view
returns (address maxBorrower_, uint256 maxT0DebtToCollateral_, uint256 noOfLoans_);
/**
* @notice Returns information about pool reserves.
* @return liquidationBondEscrowed_ Amount of liquidation bond across all liquidators.
* @return reserveAuctionUnclaimed_ Amount of claimable reserves which has not been taken in the `Claimable Reserve Auction`.
* @return reserveAuctionKicked_ Time a `Claimable Reserve Auction` was last kicked.
* @return lastKickedReserves_ Amount of reserves upon last kick, used to calculate price.
* @return totalInterestEarned_ Total interest earned by all lenders in the pool
*/
function reservesInfo()
external
view
returns (
uint256 liquidationBondEscrowed_,
uint256 reserveAuctionUnclaimed_,
uint256 reserveAuctionKicked_,
uint256 lastKickedReserves_,
uint256 totalInterestEarned_
);
/**
* @notice Returns the `pledgedCollateral` state variable.
* @return The total pledged collateral in the system, in WAD units.
*/
function pledgedCollateral() external view returns (uint256);
/**
* @notice Returns the total number of active auctions in pool.
* @return totalAuctions_ Number of active auctions.
*/
function totalAuctionsInPool() external view returns (uint256);
/**
* @notice Returns the `t0Debt` state variable.
* @dev This value should be multiplied by inflator in order to calculate current debt of the pool.
* @return The total `t0Debt` in the system, in `WAD` units.
*/
function totalT0Debt() external view returns (uint256);
/**
* @notice Returns the `t0DebtInAuction` state variable.
* @dev This value should be multiplied by inflator in order to calculate current debt in auction of the pool.
* @return The total `t0DebtInAuction` in the system, in `WAD` units.
*/
function totalT0DebtInAuction() external view returns (uint256);
/**
* @notice Mapping of addresses that can transfer `LP` to a given lender.
* @param lender_ Lender that receives `LP`.
* @param transferor_ Transferor that transfers `LP`.
* @return True if the transferor is approved by lender.
*/
function approvedTransferors(address lender_, address transferor_) external view returns (bool);
}
/**
*
*/
/**
* State Structs **
*/
/**
*
*/
/**
*
*/
/**
* Pool State **
*/
/**
*
*/
/// @dev Struct holding inflator state.
struct InflatorState {
uint208 inflator; // [WAD] pool's inflator
uint48 inflatorUpdate; // [SEC] last time pool's inflator was updated
}
/// @dev Struct holding pool interest state.
struct InterestState {
uint208 interestRate; // [WAD] pool's interest rate
uint48 interestRateUpdate; // [SEC] last time pool's interest rate was updated (not before 12 hours passed)
uint256 debt; // [WAD] previous update's debt
uint256 meaningfulDeposit; // [WAD] previous update's meaningfulDeposit
uint256 t0Debt2ToCollateral; // [WAD] utilization weight accumulator, tracks debt and collateral relationship accross borrowers
uint256 debtCol; // [WAD] previous debt squared to collateral
uint256 lupt0Debt; // [WAD] previous LUP * t0 debt
}
/// @dev Struct holding pool EMAs state.
struct EmaState {
uint256 debtEma; // [WAD] sample of debt EMA, numerator to MAU calculation
uint256 depositEma; // [WAD] sample of meaningful deposit EMA, denominator to MAU calculation
uint256 debtColEma; // [WAD] debt squared to collateral EMA, numerator to TU calculation
uint256 lupt0DebtEma; // [WAD] EMA of LUP * t0 debt, denominator to TU calculation
uint256 emaUpdate; // [SEC] last time pool's EMAs were updated
}
/// @dev Struct holding pool balances state.
struct PoolBalancesState {
uint256 pledgedCollateral; // [WAD] total collateral pledged in pool
uint256 t0DebtInAuction; // [WAD] Total debt in auction used to restrict LPB holder from withdrawing
uint256 t0Debt; // [WAD] Pool debt as if the whole amount was incurred upon the first loan
}
/// @dev Struct holding pool params (in memory only).
struct PoolState {
uint8 poolType; // pool type, can be ERC20 or ERC721
uint256 t0Debt; // [WAD] t0 debt in pool
uint256 t0DebtInAuction; // [WAD] t0 debt in auction within pool
uint256 debt; // [WAD] total debt in pool, accrued in current block
uint256 collateral; // [WAD] total collateral pledged in pool
uint256 inflator; // [WAD] current pool inflator
bool isNewInterestAccrued; // true if new interest already accrued in current block
uint256 rate; // [WAD] pool's current interest rate
uint256 quoteTokenScale; // [WAD] quote token scale of the pool. Same as quote token dust.
}
/**
*
*/
/**
* Buckets State **
*/
/**
*
*/
/// @dev Struct holding lender state.
struct Lender {
uint256 lps; // [WAD] Lender LP accumulator
uint256 depositTime; // timestamp of last deposit
}
/// @dev Struct holding bucket state.
struct Bucket {
uint256 lps; // [WAD] Bucket LP accumulator
uint256 collateral; // [WAD] Available collateral tokens deposited in the bucket
uint256 bankruptcyTime; // Timestamp when bucket become insolvent, 0 if healthy
mapping(address => Lender) lenders; // lender address to Lender struct mapping
}
/**
*
*/
/**
* Deposits State **
*/
/**
*
*/
/// @dev Struct holding deposits (Fenwick) values and scaling.
struct DepositsState {
uint256[8193] values; // Array of values in the FenwickTree.
uint256[8193] scaling; // Array of values which scale (multiply) the FenwickTree accross indexes.
}
/**
*
*/
/**
* Loans State **
*/
/**
*
*/
/// @dev Struct holding loans state.
struct LoansState {
Loan[] loans;
mapping(address => uint256) indices; // borrower address => loan index mapping
mapping(address => Borrower) borrowers; // borrower address => Borrower struct mapping
}
/// @dev Struct holding loan state.
struct Loan {
address borrower; // borrower address
uint96 t0DebtToCollateral; // [WAD] Borrower t0 debt to collateral.
}
/// @dev Struct holding borrower state.
struct Borrower {
uint256 t0Debt; // [WAD] Borrower debt time-adjusted as if it was incurred upon first loan of pool.
uint256 collateral; // [WAD] Collateral deposited by borrower.
uint256 npTpRatio; // [WAD] Np to Tp ratio at the time of last borrow or pull collateral.
}
/**
*
*/
/**
* Auctions State **
*/
/**
*
*/
/// @dev Struct holding pool auctions state.
struct AuctionsState {
uint96 noOfAuctions; // total number of auctions in pool
address head; // first address in auction queue
address tail; // last address in auction queue
uint256 totalBondEscrowed; // [WAD] total amount of quote token posted as auction kick bonds
mapping(address => Liquidation) liquidations; // mapping of borrower address and auction details
mapping(address => Kicker) kickers; // mapping of kicker address and kicker balances
}
/// @dev Struct holding liquidation state.
struct Liquidation {
address kicker; // address that initiated liquidation
uint96 bondFactor; // [WAD] bond factor used to start liquidation
uint96 kickTime; // timestamp when liquidation was started
address prev; // previous liquidated borrower in auctions queue
uint96 referencePrice; // [WAD] used to calculate auction start price
address next; // next liquidated borrower in auctions queue
uint160 bondSize; // [WAD] liquidation bond size
uint96 neutralPrice; // [WAD] Neutral Price when liquidation was started
uint256 debtToCollateral; // [WAD] Borrower debt to collateral, which is used in BPF for kicker's reward calculation
uint256 t0ReserveSettleAmount; // [WAD] Amount of t0Debt that could be settled via reserves in this auction
}
/// @dev Struct holding kicker state.
struct Kicker {
uint256 claimable; // [WAD] kicker's claimable balance
uint256 locked; // [WAD] kicker's balance of tokens locked in auction bonds
}
/**
*
*/
/**
* Reserve Auctions State **
*/
/**
*
*/
/// @dev Struct holding reserve auction state.
struct ReserveAuctionState {
uint256 kicked; // Time a Claimable Reserve Auction was last kicked.
uint256 lastKickedReserves; // [WAD] Amount of reserves upon last kick, used to calculate price.
uint256 unclaimed; // [WAD] Amount of claimable reserves which has not been taken in the Claimable Reserve Auction.
uint256 latestBurnEventEpoch; // Latest burn event epoch.
uint256 totalAjnaBurned; // [WAD] Total ajna burned in the pool.
uint256 totalInterestEarned; // [WAD] Total interest earned by all lenders in the pool.
mapping(uint256 => BurnEvent) burnEvents; // Mapping burnEventEpoch => BurnEvent.
}
/// @dev Struct holding burn event state.
struct BurnEvent {
uint256 timestamp; // time at which the burn event occured
uint256 totalInterest; // [WAD] current pool interest accumulator `PoolCommons.accrueInterest().newInterest`
uint256 totalBurned; // [WAD] burn amount accumulator
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
/**
* @title Pool Derived State
*/
interface IPoolDerivedState {
/**
* @notice Returns the exchange rate for a given bucket index.
* @param index_ The bucket index.
* @return exchangeRate_ Exchange rate of the bucket (`WAD` precision).
*/
function bucketExchangeRate(uint256 index_) external view returns (uint256 exchangeRate_);
/**
* @notice Returns the prefix sum of a given bucket.
* @param index_ The bucket index.
* @return The deposit up to given index (`WAD` precision).
*/
function depositUpToIndex(uint256 index_) external view returns (uint256);
/**
* @notice Returns the bucket index for a given debt amount.
* @param debt_ The debt amount to calculate bucket index for (`WAD` precision).
* @return Bucket index.
*/
function depositIndex(uint256 debt_) external view returns (uint256);
/**
* @notice Returns the total amount of quote tokens deposited in pool.
* @return Total amount of deposited quote tokens (`WAD` precision).
*/
function depositSize() external view returns (uint256);
/**
* @notice Returns the meaningful actual utilization of the pool.
* @return Deposit utilization (`WAD` precision).
*/
function depositUtilization() external view returns (uint256);
/**
* @notice Returns the scaling value of deposit at given index.
* @param index_ Deposit index.
* @return Deposit scaling (`WAD` precision).
*/
function depositScale(uint256 index_) external view returns (uint256);
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
/**
* @title Pool Events
*/
interface IPoolEvents {
/**
*
*/
/**
* Lender events **
*/
/**
*
*/
/**
* @notice Emitted when lender adds quote token to the pool.
* @param lender Recipient that added quote tokens.
* @param index Index at which quote tokens were added.
* @param amount Amount of quote tokens added to the pool (`WAD` precision).
* @param lpAwarded Amount of `LP` awarded for the deposit (`WAD` precision).
* @param lup `LUP` calculated after deposit.
*/
event AddQuoteToken(address indexed lender, uint256 indexed index, uint256 amount, uint256 lpAwarded, uint256 lup);
/**
* @notice Emitted when lender moves quote token from a bucket price to another.
* @param lender Recipient that moved quote tokens.
* @param from Price bucket from which quote tokens were moved.
* @param to Price bucket where quote tokens were moved.
* @param amount Amount of quote tokens moved (`WAD` precision).
* @param lpRedeemedFrom Amount of `LP` removed from the `from` bucket (`WAD` precision).
* @param lpAwardedTo Amount of `LP` credited to the `to` bucket (`WAD` precision).
* @param lup `LUP` calculated after removal.
*/
event MoveQuoteToken(
address indexed lender,
uint256 indexed from,
uint256 indexed to,
uint256 amount,
uint256 lpRedeemedFrom,
uint256 lpAwardedTo,
uint256 lup
);
/**
* @notice Emitted when lender removes quote token from the pool.
* @param lender Recipient that removed quote tokens.
* @param index Index at which quote tokens were removed.
* @param amount Amount of quote tokens removed from the pool (`WAD` precision).
* @param lpRedeemed Amount of `LP` exchanged for quote token (`WAD` precision).
* @param lup `LUP` calculated after removal.
*/
event RemoveQuoteToken(
address indexed lender, uint256 indexed index, uint256 amount, uint256 lpRedeemed, uint256 lup
);
/**
* @notice Emitted when lender claims collateral from a bucket.
* @param claimer Recipient that claimed collateral.
* @param index Index at which collateral was claimed.
* @param amount The amount of collateral (`WAD` precision for `ERC20` pools, number of `NFT` tokens for `ERC721` pools) transferred to the claimer.
* @param lpRedeemed Amount of `LP` exchanged for quote token (`WAD` precision).
*/
event RemoveCollateral(address indexed claimer, uint256 indexed index, uint256 amount, uint256 lpRedeemed);
/**
*
*/
/**
* Borrower events **
*/
/**
*
*/
/**
* @notice Emitted when borrower repays quote tokens to the pool and/or pulls collateral from the pool.
* @param borrower `msg.sender` or on behalf of sender.
* @param quoteRepaid Amount of quote tokens repaid to the pool (`WAD` precision).
* @param collateralPulled The amount of collateral (`WAD` precision for `ERC20` pools, number of `NFT` tokens for `ERC721` pools) transferred to the claimer.
* @param lup `LUP` after repay.
*/
event RepayDebt(address indexed borrower, uint256 quoteRepaid, uint256 collateralPulled, uint256 lup);
/**
*
*/
/**
* Auction events **
*/
/**
*
*/
/**
* @notice Emitted when a liquidation is initiated.
* @param borrower Identifies the loan being liquidated.
* @param debt Debt the liquidation will attempt to cover (`WAD` precision).
* @param collateral Amount of collateral up for liquidation (`WAD` precision for `ERC20` pools, number of `NFT` tokens for `ERC721` pools).
* @param bond Bond amount locked by kicker (`WAD` precision).
*/
event Kick(address indexed borrower, uint256 debt, uint256 collateral, uint256 bond);
/**
* @notice Emitted when kickers are withdrawing funds posted as auction bonds.
* @param kicker The kicker withdrawing bonds.
* @param reciever The address receiving withdrawn bond amount.
* @param amount The bond amount that was withdrawn (`WAD` precision).
*/
event BondWithdrawn(address indexed kicker, address indexed reciever, uint256 amount);
/**
* @notice Emitted when an actor uses quote token to arb higher-priced deposit off the book.
* @param borrower Identifies the loan being liquidated.
* @param index The index of the `Highest Price Bucket` used for this take.
* @param amount Amount of quote token used to purchase collateral (`WAD` precision).
* @param collateral Amount of collateral purchased with quote token (`WAD` precision).
* @param bondChange Impact of this take to the liquidation bond (`WAD` precision).
* @param isReward `True` if kicker was rewarded with `bondChange` amount, `false` if kicker was penalized.
* @dev amount / collateral implies the auction price.
*/
event BucketTake(
address indexed borrower, uint256 index, uint256 amount, uint256 collateral, uint256 bondChange, bool isReward
);
/**
* @notice Emitted when `LP` are awarded to a taker or kicker in a bucket take.
* @param taker Actor who invoked the bucket take.
* @param kicker Actor who started the auction.
* @param lpAwardedTaker Amount of `LP` awarded to the taker (`WAD` precision).
* @param lpAwardedKicker Amount of `LP` awarded to the actor who started the auction (`WAD` precision).
*/
event BucketTakeLPAwarded(
address indexed taker, address indexed kicker, uint256 lpAwardedTaker, uint256 lpAwardedKicker
);
/**
* @notice Emitted when an actor uses quote token outside of the book to purchase collateral under liquidation.
* @param borrower Identifies the loan being liquidated.
* @param amount Amount of quote token used to purchase collateral (`WAD` precision).
* @param collateral Amount of collateral purchased with quote token (for `ERC20` pool, `WAD` precision) or number of `NFT`s purchased (for `ERC721` pool).
* @param bondChange Impact of this take to the liquidation bond (`WAD` precision).
* @param isReward `True` if kicker was rewarded with `bondChange` amount, `false` if kicker was penalized.
* @dev amount / collateral implies the auction price.
*/
event Take(address indexed borrower, uint256 amount, uint256 collateral, uint256 bondChange, bool isReward);
/**
* @notice Emitted when an actor settles debt in a completed liquidation
* @param borrower Identifies the loan under liquidation.
* @param settledDebt Amount of pool debt settled in this transaction (`WAD` precision).
* @dev When `amountRemaining_ == 0`, the auction has been completed cleared and removed from the queue.
*/
event Settle(address indexed borrower, uint256 settledDebt);
/**
* @notice Emitted when auction is completed.
* @param borrower Address of borrower that exits auction.
* @param collateral Borrower's remaining collateral when auction completed (`WAD` precision).
*/
event AuctionSettle(address indexed borrower, uint256 collateral);
/**
* @notice Emitted when `NFT` auction is completed.
* @param borrower Address of borrower that exits auction.
* @param collateral Borrower's remaining collateral when auction completed.
* @param lp Amount of `LP` given to the borrower to compensate fractional collateral (if any, `WAD` precision).
* @param index Index of the bucket with `LP` to compensate fractional collateral.
*/
event AuctionNFTSettle(address indexed borrower, uint256 collateral, uint256 lp, uint256 index);
/**
* @notice Emitted when a `Claimaible Reserve Auction` is started.
* @param claimableReservesRemaining Amount of claimable reserves which has not yet been taken (`WAD` precision).
* @param auctionPrice Current price at which `1` quote token may be purchased, denominated in `Ajna`.
* @param currentBurnEpoch Current burn epoch.
*/
event KickReserveAuction(uint256 claimableReservesRemaining, uint256 auctionPrice, uint256 currentBurnEpoch);
/**
* @notice Emitted when a `Claimaible Reserve Auction` is taken.
* @param claimableReservesRemaining Amount of claimable reserves which has not yet been taken (`WAD` precision).
* @param auctionPrice Current price at which `1` quote token may be purchased, denominated in `Ajna`.
* @param currentBurnEpoch Current burn epoch.
*/
event ReserveAuction(uint256 claimableReservesRemaining, uint256 auctionPrice, uint256 currentBurnEpoch);
/**
*
*/
/**
* LP transfer events **
*/
/**
*
*/
/**
* @notice Emitted when owner increase the `LP` allowance of a spender at specified indexes with specified amounts.
* @param owner `LP` owner.
* @param spender Address approved to transfer `LP`.
* @param indexes Bucket indexes of `LP` approved.
* @param amounts `LP` amounts added (ordered by indexes, `WAD` precision).
*/
event IncreaseLPAllowance(address indexed owner, address indexed spender, uint256[] indexes, uint256[] amounts);
/**
* @notice Emitted when owner decrease the `LP` allowance of a spender at specified indexes with specified amounts.
* @param owner `LP` owner.
* @param spender Address approved to transfer `LP`.
* @param indexes Bucket indexes of `LP` approved.
* @param amounts `LP` amounts removed (ordered by indexes, `WAD` precision).
*/
event DecreaseLPAllowance(address indexed owner, address indexed spender, uint256[] indexes, uint256[] amounts);
/**
* @notice Emitted when lender removes the allowance of a spender for their `LP`.
* @param owner `LP` owner.
* @param spender Address that is having it's allowance revoked.
* @param indexes List of bucket index to remove the allowance from.
*/
event RevokeLPAllowance(address indexed owner, address indexed spender, uint256[] indexes);
/**
* @notice Emitted when lender whitelists addresses to accept `LP` from.
* @param lender Recipient that approves new owner for `LP`.
* @param transferors List of addresses that can transfer `LP` to lender.
*/
event ApproveLPTransferors(address indexed lender, address[] transferors);
/**
* @notice Emitted when lender removes addresses from the `LP` transferors whitelist.
* @param lender Recipient that approves new owner for `LP`.
* @param transferors List of addresses that won't be able to transfer `LP` to lender anymore.
*/
event RevokeLPTransferors(address indexed lender, address[] transferors);
/**
* @notice Emitted when a lender transfers their `LP` to a different address.
* @dev Used by `PositionManager.memorializePositions()`.
* @param owner The original owner address of the position.
* @param newOwner The new owner address of the position.
* @param indexes Array of price bucket indexes at which `LP` were transferred.
* @param lp Amount of `LP` transferred (`WAD` precision).
*/
event TransferLP(address owner, address newOwner, uint256[] indexes, uint256 lp);
/**
*
*/
/**
* Pool common events **
*/
/**
*
*/
/**
* @notice Emitted when `LP` are forfeited as a result of the bucket losing all assets.
* @param index The index of the bucket.
* @param lpForfeited Amount of `LP` forfeited by lenders (`WAD` precision).
*/
event BucketBankruptcy(uint256 indexed index, uint256 lpForfeited);
/**
* @notice Emitted when a flashloan is taken from pool.
* @param receiver The address receiving the flashloan.
* @param token The address of token flashloaned from pool.
* @param amount The amount of tokens flashloaned from pool (token precision).
*/
event Flashloan(address indexed receiver, address indexed token, uint256 amount);
/**
* @notice Emitted when a loan `Np to Tp ratio` is restamped.
* @param borrower Identifies the loan to update the `Np to Tp ratio`.
*/
event LoanStamped(address indexed borrower);
/**
* @notice Emitted when pool interest rate is reset. This happens when `interest rate > 10%` and `debtEma < 5%` of `depositEma`
* @param oldRate Old pool interest rate.
* @param newRate New pool interest rate.
*/
event ResetInterestRate(uint256 oldRate, uint256 newRate);
/**
* @notice Emitted when pool interest rate is updated.
* @param oldRate Old pool interest rate.
* @param newRate New pool interest rate.
*/
event UpdateInterestRate(uint256 oldRate, uint256 newRate);
/**
* @notice Emitted when interest accural or update interest overflows.
*/
event InterestUpdateFailure();
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
/**
* @title Pool Errors.
*/
interface IPoolErrors {
/**
*
*/
/**
* Common Pool Errors **
*/
/**
*
*/
/**
* @notice Adding liquidity above current auction price.
*/
error AddAboveAuctionPrice();
/**
* @notice The action cannot be executed on an active auction.
*/
error AuctionActive();
/**
* @notice Attempted auction to clear doesn't meet conditions.
*/
error AuctionNotClearable();
/**
* @notice Auction does not meet requirements to take liquidity.
*/
error AuctionNotTakeable();
/**
* @notice Head auction should be cleared prior of executing this action.
*/
error AuctionNotCleared();
/**
* @notice The auction price is greater than the arbed bucket price.
*/
error AuctionPriceGtBucketPrice();
/**
* @notice Pool already initialized.
*/
error AlreadyInitialized();
/**
* @notice Borrower is attempting to create or modify a loan such that their loan's quote token would be less than the pool's minimum debt amount.
*/
error AmountLTMinDebt();
/**
* @notice Recipient of borrowed quote tokens doesn't match the caller of the `drawDebt` function.
*/
error BorrowerNotSender();
/**
* @notice Borrower has a healthy over-collateralized position.
*/
error BorrowerOk();
/**
* @notice Borrower is attempting to borrow more quote token than they have collateral for.
*/
error BorrowerUnderCollateralized();
/**
* @notice Operation cannot be executed in the same block when bucket becomes insolvent.
*/
error BucketBankruptcyBlock();
/**
* @notice User attempted to merge collateral from a lower price bucket into a higher price bucket.
*/
error CannotMergeToHigherPrice();
/**
* @notice User attempted an operation which does not exceed the dust amount, or leaves behind less than the dust amount.
*/
error DustAmountNotExceeded();
/**
* @notice Callback invoked by `flashLoan` function did not return the expected hash (see `ERC-3156` spec).
*/
error FlashloanCallbackFailed();
/**
* @notice Balance of pool contract before flashloan is different than the balance after flashloan.
*/
error FlashloanIncorrectBalance();
/**
* @notice Pool cannot facilitate a flashloan for the specified token address.
*/
error FlashloanUnavailableForToken();
/**
* @notice User is attempting to move or pull more collateral than is available.
*/
error InsufficientCollateral();
/**
* @notice Lender is attempting to move or remove more collateral they have claim to in the bucket.
* @notice Lender is attempting to remove more collateral they have claim to in the bucket.
* @notice Lender must have enough `LP` to claim the desired amount of quote from the bucket.
*/
error InsufficientLP();
/**
* @notice Bucket must have more quote available in the bucket than the lender is attempting to claim.
*/
error InsufficientLiquidity();
/**
* @notice When increasing / decreasing `LP` allowances indexes and amounts arrays parameters should have same length.
*/
error InvalidAllowancesInput();
/**
* @notice When transferring `LP` between indices, the new index must be a valid index.
*/
error InvalidIndex();
/**
* @notice The amount used for performed action should be greater than `0`.
*/
error InvalidAmount();
/**
* @notice Borrower is attempting to borrow more quote token than is available before the supplied `limitIndex`.
*/
error LimitIndexExceeded();
/**
* @notice When moving quote token `HTP` must stay below `LUP`.
* @notice When removing quote token `HTP` must stay below `LUP`.
*/
error LUPBelowHTP();
/**
* @notice From index and to index arguments to move are the same.
*/
error MoveToSameIndex();
/**
* @notice Owner of the `LP` must have approved the new owner prior to transfer.
*/
error NoAllowance();
/**
* @notice Actor is attempting to take or clear an inactive auction.
*/
error NoAuction();
/**
* @notice No pool reserves are claimable.
*/
error NoReserves();
/**
* @notice Actor is attempting to take or clear an inactive reserves auction.
*/
error NoReservesAuction();
/**
* @notice Lender must have non-zero `LP` when attemptign to remove quote token from the pool.
*/
error NoClaim();
/**
* @notice Borrower has no debt to liquidate.
* @notice Borrower is attempting to repay when they have no outstanding debt.
*/
error NoDebt();
/**
* @notice Actor is attempting to kick with bucket price below the `LUP`.
*/
error PriceBelowLUP();
/**
* @notice Lender is attempting to remove quote tokens from a bucket that exists above active auction debt from top-of-book downward.
*/
error RemoveDepositLockedByAuctionDebt();
/**
* @notice User attempted to kick off a new auction less than `2` weeks since the last auction completed.
*/
error ReserveAuctionTooSoon();
/**
* @notice Current block timestamp has reached or exceeded a user-provided expiration.
*/
error TransactionExpired();
/**
* @notice The address that transfer `LP` is not approved by the `LP` receiving address.
*/
error TransferorNotApproved();
/**
* @notice Owner of the `LP` attemps to transfer `LP` to same address.
*/
error TransferToSameOwner();
/**
* @notice The DebtToCollateral of the loan to be inserted in loans heap is zero.
*/
error ZeroDebtToCollateral();
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
import {IERC3156FlashBorrower} from "./IERC3156FlashBorrower.sol";
interface IERC3156FlashLender {
/**
* @dev The amount of currency available to be lent.
* @param token_ The loan currency.
* @return The amount of `token` that can be borrowed (token precision).
*/
function maxFlashLoan(address token_) external view returns (uint256);
/**
* @dev The fee to be charged for a given loan.
* @param token_ The loan currency.
* @param amount_ The amount of tokens lent (token precision).
* @return The amount of `token` to be charged for the loan (token precision), on top of the returned principal .
*/
function flashFee(address token_, uint256 amount_) external view returns (uint256);
/**
* @dev Initiate a flash loan.
* @param receiver_ The receiver of the tokens in the loan, and the receiver of the callback.
* @param token_ The loan currency.
* @param amount_ The amount of tokens lent (token precision).
* @param data_ Arbitrary data structure, intended to contain user-defined parameters.
* @return `True` when successful flashloan, `false` otherwise.
*/
function flashLoan(IERC3156FlashBorrower receiver_, address token_, uint256 amount_, bytes calldata data_)
external
returns (bool);
}// SPDX-License-Identifier: BUSL-1.1
// Author: Lendvest
pragma solidity 0.8.20;
interface IERC3156FlashBorrower {
/**
* @dev Receive a flash loan.
* @param initiator The initiator of the loan.
* @param token The loan currency.
* @param amount The amount of tokens lent (token precision).
* @param fee The additional amount of tokens to repay.
* @param data Arbitrary data structure, intended to contain user-defined parameters.
* @return The `keccak256` hash of `ERC3156FlashBorrower.onFlashLoan`
*/
function onFlashLoan(address initiator, address token, uint256 amount, uint256 fee, bytes calldata data)
external
returns (bytes32);
}{
"remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@chainlink/contracts/=lib/chainlink/contracts/",
"forge-std/=lib/forge-std/src/",
"@balancer/=lib/balancer-v2-monorepo/pkg/interfaces/contracts/",
"@prb/math/=lib/prb-math/src/",
"@balancer-labs/=lib/balancer-v2-monorepo/../../node_modules/@balancer-labs/",
"balancer-v2-monorepo/=lib/balancer-v2-monorepo/",
"chainlink/=lib/chainlink/",
"ds-test/=lib/balancer-v2-monorepo/../../pvt/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"prb-math/=lib/prb-math/src/"
],
"optimizer": {
"enabled": true,
"runs": 1
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "shanghai",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_LVLidoVault","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidInput","type":"error"},{"inputs":[],"name":"OnlyForwarder","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"taskId","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"metadata","type":"bytes"}],"name":"CreReportReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"ForwarderAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"name":"FundsQueued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"requestId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"response","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"rate","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"err","type":"bytes"}],"name":"OCRResponse","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"redemptionRate","type":"uint256"}],"name":"RedemptionRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"}],"name":"TermEnded","type":"event"},{"inputs":[],"name":"FACTOR_COLLATERAL_INCREASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LVLidoVault","outputs":[{"internalType":"contract ILVLidoVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TRANCHES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRICE_FEED_DECIMALS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"sumLiquidityRates_1e27","type":"uint256"},{"internalType":"uint256","name":"sumVariableBorrowRates_1e27","type":"uint256"},{"internalType":"uint256","name":"numRates","type":"uint256"}],"name":"fulfillRateFromCRE","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"getWstethToWeth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lidoClaimDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lowerBoundRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lvLidoVaultUpkeeper","outputs":[{"internalType":"contract LVLidoVaultUpkeeper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"metadata","type":"bytes"},{"internalType":"bytes","name":"report","type":"bytes"}],"name":"onReport","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"performTask","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"performData","type":"bytes"}],"name":"performUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"poolInfoUtils","outputs":[{"internalType":"contract IPoolInfoUtils","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"s_forwarderAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"s_lastUpkeepTimeStamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"forwarderAddress","type":"address"}],"name":"setForwarderAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_upkeeper","type":"address"}],"name":"setLVLidoVaultUpkeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_needed","type":"bool"}],"name":"setUpdateRateNeeded","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateRateNeeded","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"upperBoundRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60808060405234620001b4576020816200353c8038038091620000238285620001b8565b833981010312620001b457516001600160a01b0380821691829003620001b45733156200019c575f9182549160018060a01b03199233848216178555604051913391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08680a36301afd7c160e11b8152602081600481737f39c581f595b53c5cb19bd0b3f8da6c935e2ca05afa908115620001915784916200013f575b50735f4ec3df9cbd43714fe2740f5e3616155c5b84199350600355600160ff19600854161760085581600154161760015573cfe54b5cd566ab89272946f602d76ea879cab4a881600454161760045560055416176005556611c37937e0800060075567016345785d8a000060065560405161334b9081620001f18239f35b90506020813d821162000188575b816200015c60209383620001b8565b810103126200018457735f4ec3df9cbd43714fe2740f5e3616155c5b84199350515f620000c1565b8380fd5b3d91506200014d565b6040513d86823e3d90fd5b604051631e4fbdf760e01b81525f6004820152602490fd5b5f80fd5b601f909101601f19168101906001600160401b03821190821017620001dc57604052565b634e487b7160e01b5f52604160045260245ffdfe6080604081815260049182361015610015575f80fd5b5f92833560e01c91826301ffc9a714610cb1575081630cc9c94d14610c925781632b1f03e914610c3b5781633070fbf814610c125781633a84701714610be95781634585e33b14610b7c5781634ed94b2414610b605781636e04ff0d14610b06578163715018a614610abf57816377bac89d14610a9657816378f42cc614610a72578163805f21321461077957816386c1b640146106e457816389506a44146106c85781638da5cb5b146106a0578163907f25791461067157816391ff95d1146106525781639bb1740514610630578163a298f6eb146104a4578163a314e4ae14610485578163b5334dfb14610409578163d777cc6d1461030e578163e5d6f8ce146101d4578163f2fde38b14610155575063f7dcf90414610135575f80fd5b346101515781600319360112610151576020905162093a808152f35b5080fd5b919050346101d05760203660031901126101d0576001600160a01b038235818116939192908490036101cc57610189610d77565b83156101b65750505f548260018060a01b03198216175f55165f805160206132f68339815191525f80a380f35b51631e4fbdf760e01b8152908101849052602490fd5b8480fd5b8280fd5b919050346101d057826003193601126101d0578051636e04ff0d60e01b8152838180610201868201612efa565b0381305afa90811561030457849085926102df575b50156102a9578390600a549260018060a01b0319943086861617600a55303b156102a5576020849161025f84519586938493634585e33b60e01b85528401526024830190610d52565b038183305af190811561029c5750610288575b505060018060a01b031690600a541617600a5580f35b61029190610da2565b6101d057825f610272565b513d84823e3d90fd5b8380fd5b815162461bcd60e51b8152602081850152601060248201526f139bc81d5c1ad9595c081b995959195960821b6044820152606490fd5b90506102fd91503d8086833e6102f58183610de4565b810190612e81565b905f610216565b82513d86823e3d90fd5b919050346101d05760203660031901126101d05781356001600160a01b0381811693848303610405578060208360015416865192838092638da5cb5b60e01b82525afa9081156103fb5761036e91849189916103cd575b50163314612f17565b84156103bf5750907f039ad854736757070884dd787ef1a7f58db33546639d1f3efddcf4a33fb8997e916103ab600a549451928392861683611018565b0390a16001600160a01b03191617600a5580f35b835163b4fa3fb360e01b8152fd5b6103ee915060203d81116103f4575b6103e68183610de4565b810190610fd7565b5f610365565b503d6103dc565b85513d89823e3d90fd5b8580fd5b9050346101d05760203660031901126101d0578035908115158092036102a5576001548351638da5cb5b60e01b81526001600160a01b039290916020918391829086165afa90811561047b5761046a93945085916103cd5750163314612f17565b60ff80196008541691161760085580f35b84513d87823e3d90fd5b5050346101515781600319360112610151576020906007549051908152f35b83833461015157602091826003193601126105f0575082548151633fabe5a360e21b80825260a095926001600160a01b039290879082908690829087165afa908115610626575f91610604575b5084516301afd7c160e11b815286818681737f39c581f595b53c5cb19bd0b3f8da6c935e2ca05afa9081156105fa5790889392915f916105bb575b5090670de0b6b3a764000061054561054b938835610e50565b04610e50565b92600554169385518095819382525afa9182156105b1576105769394955f9361057d575b5050610e77565b9051908152f35b61059d929350803d106105aa575b6105958183610de4565b810190610e1b565b505050905090858061056f565b503d61058b565b83513d5f823e3d90fd5b919293508782813d83116105f3575b6105d48183610de4565b810103126105f057505187929190670de0b6b3a764000061052c565b80fd5b503d6105ca565b86513d5f823e3d90fd5b61061b9150873d89116105aa576105958183610de4565b5050509050876104f1565b85513d5f823e3d90fd5b5050346101515781600319360112610151576020905166271471148780008152f35b5050346101515781600319360112610151576020906006549051908152f35b505034610151578160031936011261015157602090517330c5ef2997d6a882de52c4ec01b6d0a5e5b4faae8152f35b505034610151578160031936011261015157905490516001600160a01b039091168152602090f35b5050346101515781600319360112610151576020905160038152f35b9050346101d05760203660031901126101d05780356001600160a01b0381811693918490036101cc578260208260015416845192838092638da5cb5b60e01b82525afa90811561076f5790610743929187916103cd5750163314612f17565b8215610762575050600280546001600160a01b03191691909117905580f35b5163b4fa3fb360e01b8152fd5b83513d88823e3d90fd5b919050346101d057806003193601126101d0576001600160401b039180358381116101cc576107ab9036908301610d04565b93602435908111610405576107c39036908401610d04565b602092919295868210610a625787908483810103938885126101d0577f73135abe2f319d59aa80a5fc0d2fc64396089064a520a75cbd9338de75026b0782898b8260dd978b35978897855196858896875286015285850137828201840152601f01601f19168101030190a20361096e576080111561094f575050426009556001548251631627391760e11b81526001600160a01b03909116939081818481885afa908115610945575f91610918575b505f915f805160206132b683398151915291704352453a204e6f2072617465206461746160781b865192830152601182526108ac82610dc9565b6108ba865192839283612f62565b0390a2823b15610914575f6024819282855196879485936369ea177160e01b85528401525af190811561090b57506108fc575b5060ff19600854166008555b80f35b61090590610da2565b5f6108ed565b513d5f823e3d90fd5b5f80fd5b908282813d831161093e575b61092e8183610de4565b810103126105f05750515f610872565b503d610924565b84513d5f823e3d90fd5b6080919250126102a5576108f992606082013592820135910135612f86565b50508251636e04ff0d60e01b8152919392905084828061098f818801612efa565b0381305afa918215610a585785908693610a39575b506109b1575b5050505080f35b84600a549360018060a01b0319953087871617600a55303b156101d0576109ef84519586938493634585e33b60e01b85528401526024830190610d52565b038183305af1908115610a305750610a1d575b5060018060a01b031690600a541617600a555f8080806109aa565b610a2990939193610da2565b915f610a02565b513d86823e3d90fd5b9050610a509192503d8087833e6102f58183610de4565b91905f6109a4565b81513d87823e3d90fd5b855163b4fa3fb360e01b81528590fd5b50503461015157816003193601126101515760209060ff6008541690519015158152f35b50503461015157816003193601126101515760025490516001600160a01b039091168152602090f35b83346105f057806003193601126105f057610ad8610d77565b80546001600160a01b03198116825581906001600160a01b03165f805160206132f68339815191528280a380f35b9050346101d05760203660031901126101d05780356001600160401b0381116102a557610b3591369101610d04565b5050610b5c610b42611178565b839291925193849315158452806020850152830190610d52565b0390f35b5050346101515781600319360112610151576020905160088152f35b8383346101515760203660031901126101515782356001600160401b0381116101d057610bac9036908501610d04565b9160018060a01b03600a541633141580610bdf575b610bd05750906108f991611e28565b5163a73d374160e01b81528490fd5b5030331415610bc1565b50503461015157816003193601126101515760015490516001600160a01b039091168152602090f35b505034610151578160031936011261015157600a5490516001600160a01b039091168152602090f35b919050346101d05760603660031901126101d057600a546001600160a01b031633141580610c88575b610c7b57506108f990604435906024359035612f86565b5163a73d374160e01b8152fd5b5030331415610c64565b5050346101515781600319360112610151576020906009549051908152f35b8491346101d05760203660031901126101d0573563ffffffff60e01b81168091036101d0576020925063402f909960e11b8114908115610cf3575b5015158152f35b6301ffc9a760e01b14905083610cec565b9181601f84011215610914578235916001600160401b038311610914576020838186019501011161091457565b5f5b838110610d425750505f910152565b8181015183820152602001610d33565b90602091610d6b81518092818552858086019101610d31565b601f01601f1916010190565b5f546001600160a01b03163303610d8a57565b60405163118cdaa760e01b8152336004820152602490fd5b6001600160401b038111610db557604052565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b03821117610db557604052565b601f909101601f19168101906001600160401b03821190821017610db557604052565b51906001600160501b038216820361091457565b908160a091031261091457610e2f81610e07565b91602082015191604081015191610e4d608060608401519301610e07565b90565b81810292918115918404141715610e6357565b634e487b7160e01b5f52601160045260245ffd5b8115610e81570490565b634e487b7160e01b5f52601260045260245ffd5b6004805460408051633fabe5a360e21b80825291949360a0936001600160a01b039392918591839190829087165afa9081156105fa575f91610fb5575b5085516301afd7c160e11b8152602081600481737f39c581f595b53c5cb19bd0b3f8da6c935e2ca05afa908115610fab575f91610f7a575b50670de0b6b3a764000081810204809103610e63578491610f2a91610e50565b926005541691600487518094819382525afa92831561062657610e4d9495505f93610f56575050610e77565b610f6d929350803d106105aa576105958183610de4565b5050509050905f8061056f565b906020823d8211610fa3575b81610f9360209383610de4565b810103126105f05750515f610f0a565b3d9150610f86565b87513d5f823e3d90fd5b610fcc9150843d86116105aa576105958183610de4565b50505090505f610ed2565b9081602091031261091457516001600160a01b03811681036109145790565b9190826080910312610914578151916020810151916060604083015192015190565b6001600160a01b0391821681529116602082015260400190565b91908201809211610e6357565b81810392915f138015828513169184121617610e6357565b90670de0b6b3a764000091828102928184051490151715610e6357565b8115610e8157600160ff1b81145f19831416610e63570590565b5190811515820361091457565b8051156110a85760200190565b634e487b7160e01b5f52603260045260245ffd5b9060209081838203126109145782516001600160401b0393848211610914570181601f82011215610914578051938411610db5578360051b906040519461110585840187610de4565b85528380860192820101928311610914578301905b828210611128575050505090565b8151815290830190830161111a565b9081518082526020808093019301915f5b828110611156575050505090565b835185529381019392810192600101611148565b5f198114610e635760010190565b6001546040516316f0115b60e01b81526001600160a01b0390911691602082600481865afa801561141c57836080916111d7945f91611e09575b5060405163035d5e7f60e31b815294859283926001600160a01b031660048401611018565b03817330c5ef2997d6a882de52c4ec01b6d0a5e5b4faae5afa91821561141c575f92611de5575b5060ff6008541680611d24575b80611d1b575b611d005761121d610e95565b60405163c865d2a360e01b8082529190602081600481895afa90811561141c575f91611ccc575b50611252906112579261103f565b611057565b90604051908152602081600481885afa90811561141c575f91611c98575b5061127f91611074565b91604051636203c27d60e01b8152602081600481885afa90811561141c575f91611c66575b5060405163968dc49d60e01b815290602082600481895afa91821561141c575f92611c32575b506627147114878000828102048203610e63576112f0916627147114878000029061103f565b506040516375f4ed0f60e11b8152602081600481885afa90811561141c575f91611bf8575b5080611b37575b80611b2e575b156116385750604051636203c27d60e01b81525f9190602081600481885afa90811561162d5783916115fb575b5060405163968dc49d60e01b815293602085600481895afa9485156115f05784956115bc575b5092905b8312806115a9575b156114275761138f9061116a565b91604051636203c27d60e01b8152602081600481895afa90811561141c575f916113ea575b506113bf8486611032565b80662714711487800002906627147114878000820403610e63576113e29161103f565b909290611379565b906020823d602011611414575b8161140460209383610de4565b810103126105f05750515f6113b4565b3d91506113f7565b6040513d5f823e3d90fd5b6040516311420fdb60e11b80825292949350602081600481895afa90811561141c575f91611577575b508115159081611561575b81611557575b501561152a57508160030360038111610e635791600314611521575b60209060046040518096819382525afa92831561141c575f936114ed575b506114a69192610e77565b6114d0575b60405190602082016001600160401b03811183821017610db5576040525f82525f9190565b604051905f6020830152602082526114e782610dc9565b60019190565b6020813d602011611519575b8161150660209383610de4565b810103126102a5575192506114a661149b565b3d91506114f9565b6001915061147d565b600393945061153a929150611032565b106114ab576040519060036020830152602082526114e782610dc9565b905015155f611461565b9050600361156f8584611032565b11159061145b565b906020823d6020116115a1575b8161159160209383610de4565b810103126105f05750515f611450565b3d9150611584565b5060036115b68583611032565b10611381565b9094506020813d6020116115e8575b816115d860209383610de4565b810103126102a55751935f611375565b3d91506115cb565b6040513d86823e3d90fd5b90506020813d602011611625575b8161161660209383610de4565b810103126101d057515f61134f565b3d9150611609565b6040513d85823e3d90fd5b6040516375f4ed0f60e11b81529193925090602081600481865afa90811561141c575f91611af4575b5080611a33575b806119cd575b61167a575b50506114ab565b61169757506040519060026020830152602082526114e782610dc9565b60405163d59add2360e01b8152602081600481855afa90811561141c575f91611993575b506117be576040516316f0115b60e01b8152602081600481855afa91821561141c57611710926080925f9161179f575b5060405163035d5e7f60e31b815293849283926001600160a01b031660048401611018565b03817330c5ef2997d6a882de52c4ec01b6d0a5e5b4faae5afa90811561141c575f9161176d575b5015611755576040519060016020830152602082526114e782610dc9565b6040519060026020830152602082526114e782610dc9565b61178f915060803d608011611798575b6117878183610de4565b810190610ff6565b5050505f611737565b503d61177d565b6117b8915060203d6020116103f4576103e68183610de4565b5f6116eb565b604051632937571f60e11b815273889edc2edab5f40e902b864ad4d7ade8e412f9b1602082600481845afa91821561141c575f9261195f575b50600460206040519461180986610dc9565b600186528136818801376040516236b65760e11b815292839182905afa90811561141c575f9161192d575b5061183e8461109b565b525f604051808094633155f1fd60e11b8252606060048301526118646064830188611137565b906001602484015260448301520381845afa90811561141c576118c6935f93849361190e575b506118b4906040519586948593849363192f225b60e31b8552604060048601526044850190611137565b83810360031901602485015290611137565b03915afa801561141c576118e1915f916118ed575b5061109b565b51611755575f80611673565b611908913d8091833e6119008183610de4565b8101906110bc565b5f6118db565b611926906118b492943d8091833e6119008183610de4565b929061188a565b906020823d602011611957575b8161194760209383610de4565b810103126105f05750515f611834565b3d915061193a565b90916020823d60201161198b575b8161197a60209383610de4565b810103126105f0575051905f6117f7565b3d915061196d565b906020823d6020116119c5575b816119ad60209383610de4565b810103126105f057506119bf9061108e565b5f6116bb565b3d91506119a0565b5060405163e3ef710b60e01b8152602081600481865afa90811561141c575f916119f9575b501561166e565b906020823d602011611a2b575b81611a1360209383610de4565b810103126105f05750611a259061108e565b5f6119f2565b3d9150611a06565b506040516315e5a1e560e01b8152602081600481865afa90811561141c575f91611ac2575b5060405163d83d324160e01b8152602081600481875afa90811561141c575f91611a8e575b50611a8791611032565b4211611668565b906020823d602011611aba575b81611aa860209383610de4565b810103126105f0575051611a87611a7d565b3d9150611a9b565b906020823d602011611aec575b81611adc60209383610de4565b810103126105f05750515f611a58565b3d9150611acf565b906020823d602011611b26575b81611b0e60209383610de4565b810103126105f05750611b209061108e565b5f611661565b3d9150611b01565b50801515611322565b506040516315e5a1e560e01b8152602081600481885afa90811561141c575f91611bc6575b5060405163d83d324160e01b8152602081600481895afa90811561141c575f91611b92575b50611b8b91611032565b421061131c565b906020823d602011611bbe575b81611bac60209383610de4565b810103126105f0575051611b8b611b81565b3d9150611b9f565b906020823d602011611bf0575b81611be060209383610de4565b810103126105f05750515f611b5c565b3d9150611bd3565b906020823d602011611c2a575b81611c1260209383610de4565b810103126105f05750611c249061108e565b5f611315565b3d9150611c05565b90916020823d602011611c5e575b81611c4d60209383610de4565b810103126105f0575051905f6112ca565b3d9150611c40565b906020823d602011611c90575b81611c8060209383610de4565b810103126105f05750515f6112a4565b3d9150611c73565b906020823d602011611cc4575b81611cb260209383610de4565b810103126105f057505161127f611275565b3d9150611ca5565b906020823d602011611cf8575b81611ce660209383610de4565b810103126105f0575051611257611244565b3d9150611cd9565b9150506040519060dd6020830152602082526114e782610dc9565b50811515611211565b506040516315e5a1e560e01b8152602081600481875afa90811561141c575f91611db3575b5060405163d83d324160e01b8152602081600481885afa90811561141c575f91611d7f575b50611d7891611032565b421161120b565b906020823d602011611dab575b81611d9960209383610de4565b810103126105f0575051611d78611d6e565b3d9150611d8c565b906020823d602011611ddd575b81611dcd60209383610de4565b810103126105f05750515f611d49565b3d9150611dc0565b611dff91925060803d608011611798576117878183610de4565b505050905f6111fe565b611e22915060203d6020116103f4576103e68183610de4565b5f6111b2565b91905f908015612e6f578390602094859181010312610151573560018060a01b039360019085825416956040928351946316f0115b60e01b8652600495848188818d5afa90811561217357611ea0929185918a91612e52575b50169060808b88518095819263035d5e7f60e31b8352868d8401611018565b03817330c5ef2997d6a882de52c4ec01b6d0a5e5b4faae5afa928315612320578993612e2f575b5060dd811480612e23575b80612e1a575b612d2157869798999a60609697519687809463ca103d1560e01b8252838d8301526024998a915afa9283156121b1578b93612cec575b5088516375f4ed0f60e11b808252919089818d81855afa908115612206578d91612cb7575b5080612bfe575b80612bf5575b156125e8575050505050611f52610e95565b9382825416865163c865d2a360e01b9081815283818b81865afa9081156121b1578b916125b9575b50611252611f88918961103f565b885191825283828b81865afa9081156121b1578b91612588575b611fac9250611074565b8751636203c27d60e01b8082528b9392909185818d81875afa90811561220657908792918e91612557575b509092915b6123e6575b50505081151580612381575b8061232a575b1561226057508254875163968dc49d60e01b808252929186169084818c81855afa908115612256578c91612229575b5080600303600381116122175790600314612210575b89516311420fdb60e11b81529085828d81865afa8015612206578d906121d7575b6120639250610e77565b80612077575b505050505050505050505050565b813b156121d3578b90888c838d519586948593631f22aa2f60e01b85528401525af180156121b157908b916121bb575b50508484541692885192835280838b81875afa9081156121b1578b91612181575b506120d39250611032565b813b1561217d57889185839289519485938492631f49d99760e31b84528d8401525af180156121735790889161215b575b5050541693843b156104055790859291838551968794859363e7cdfb5d60e01b85528401525af190811561029c5750612147575b80808080808080808080612069565b6121518291610da2565b6105f05780612138565b61216490610da2565b61216f57865f612104565b8680fd5b86513d8a823e3d90fd5b8880fd5b905082813d83116121aa575b6121978183610de4565b81010312610914576120d391515f6120c8565b503d61218d565b89513d8d823e3d90fd5b6121c490610da2565b6121cf57895f6120a7565b8980fd5b8b80fd5b508582813d83116121ff575b6121ed8183610de4565b81010312610914576120639151612059565b503d6121e3565b8b513d8f823e3d90fd5b5084612038565b634e487b7160e01b8d5260118c52888dfd5b90508481813d831161224f575b6122408183610de4565b8101031261091457515f612022565b503d612236565b8a513d8e823e3d90fd5b979350919450855163968dc49d60e01b8152818185818b5afa9182156123205789926122f0575b505061229590600392611032565b10156122a4575b505050505050565b843b15610405579085929183855196879485936369e4921360e01b85528401525af190811561029c57506122dc575b8080808061229c565b6122e68291610da2565b6105f057806122d3565b90809250813d8311612319575b6123078183610de4565b81010312610914575181612295612287565b503d6122fd565b87513d8b823e3d90fd5b5087516311420fdb60e11b815283818b81855afa9081156121b1578b91612354575b501515611ff3565b90508381813d831161237a575b61236b8183610de4565b8101031261091457515f61234c565b503d612361565b50875163968dc49d60e01b815283818b81855afa9081156121b1578b916123b7575b506123b060039184611032565b1115611fed565b90508381813d83116123df575b6123ce8183610de4565b8101031261091457516123b06123a3565b503d6123c4565b8294919212806124f3575b156124ec576123ff9061116a565b92895182815285818d81875afa908115612206578d916124bf575b508a5163968dc49d60e01b815286818e81885afa80156124b35786908f90612482575b6124479250611032565b9066271471148780009180830292830403612470578792916124689161103f565b909291611fdc565b634e487b7160e01b8e5260118d52898efd5b50508681813d83116124ac575b6124998183610de4565b810103126109145785612447915161243d565b503d61248f565b8e8d51903d90823e3d90fd5b90508581813d83116124e5575b6124d68183610de4565b8101031261091457515f61241a565b503d6124cc565b9280611fe1565b50895163968dc49d60e01b815285818d81875afa908115612206578d91612528575b5061252260039183611032565b106123f1565b90508581813d8311612550575b61253f8183610de4565b810103126109145751612522612515565b503d612535565b809350878092503d8311612581575b6125708183610de4565b81010312610914578691515f611fd7565b503d612566565b90508382813d83116125b2575b61259f8183610de4565b8101031261091457611fac915190611fa2565b503d612595565b90508381813d83116125e1575b6125d08183610de4565b810103126109145751611252611f7a565b503d6125c6565b899a959b99989796985191825287828781845afa918215612bb257869189918c94612bbc575b5083612ae0575b83612ad2575b83612a6f575b505050612635575b50505050505050505050565b89811480612a66575b156129db5750505083875416938651631627391760e11b815284818481895afa9081156128fb5787916129ae575b5087516315e5a1e560e01b8152858185818a5afa908115612961578891612981575b5042039042821161296f5762093a80820180921161296f576301e13380916126b591610e50565b04946126bf610e95565b90803b1561296b57878091868b518094819363e7cdfb5d60e01b8352878a8401525af180156129615761294e575b507fcb505eb4841d5e2273dccaff5e53d23e263e5ca97c72458c2b68a9ca652b70dd858951838152a181895416958851634550079d60e01b8152868186818b5afa908115612944578991612917575b50670de0b6b3a7640000918201809211612905578892612760889361276593610e50565b610e77565b9660648a518094819363f12f845560e01b8352737f39c581f595b53c5cb19bd0b3f8da6c935e2ca08984015273889edc2edab5f40e902b864ad4d7ade8e412f9b18a8401528b60448401525af19081156128fb5787916128c6575b50156128915790838681949389519a6127d88c610dc9565b808c528336818e0137886127eb8d61109b565b525416926128108a519b8c968795869463324c1d6d60e21b8652850152830190611137565b03925af1928315612886579261284a575b5f805160206132d683398151915294508351928352820152a15b5f808080808080808080612629565b80925084813d831161287f575b6128618183610de4565b81010312610914575f805160206132d6833981519152935191612821565b503d612857565b8451903d90823e3d90fd5b5060116064928488519362461bcd60e51b85528401528201527020b8383937bb30b6103330b4b63ab9329760791b6044820152fd5b90508481813d83116128f4575b6128dd8183610de4565b8101031261216f576128ee9061108e565b5f6127c0565b503d6128d3565b88513d89823e3d90fd5b634e487b7160e01b8952601185528589fd5b90508681813d831161293d575b61292e8183610de4565b8101031261091457515f61273c565b503d612924565b8a513d8b823e3d90fd5b61295a90979197610da2565b955f6126ed565b89513d8a823e3d90fd5b8780fd5b634e487b7160e01b8852601184528488fd5b90508581813d83116129a7575b6129988183610de4565b8101031261296b57515f61268e565b503d61298e565b90508481813d83116129d4575b6129c58183610de4565b8101031261216f57515f61266c565b503d6129bb565b6002919394989796929550146129f9575b505050505050505061283b565b6002541690813b156101cc5791846044928195948851998a968795637aac8f3760e11b87528601528401525af1918215612a5c575050612a4d575b5060ff1960085416176008555f808080808080806129ec565b612a5690610da2565b5f612a34565b51903d90823e3d90fd5b5082151561263e565b8c5163e3ef710b60e01b815293509091839182905afa908115612944578991612a9d575b501584875f612621565b90508681813d8311612acb575b612ab48183610de4565b8101031261217d57612ac59061108e565b5f612a93565b503d612aaa565b60085460ff1615935061261b565b9250505089516315e5a1e560e01b815287818781855afa908115612bb2578a91612b85575b508a5163d83d324160e01b81529088828881865afa918215612b7b5791899188938d92612b40575b5090612b3891611032565b421192612615565b92839194508092503d8311612b74575b612b5a8183610de4565b81010312612b7057612b3887928a925191612b2d565b8a80fd5b503d612b50565b8c513d8d823e3d90fd5b90508781813d8311612bab575b612b9c8183610de4565b810103126121cf57515f612b05565b503d612b92565b8b513d8c823e3d90fd5b9250925081813d8311612bee575b612bd48183610de4565b810103126121cf5787612be7879261108e565b925f61260e565b503d612bca565b50841515611f40565b5089516315e5a1e560e01b815289818d81855afa908115612206578d91612c86575b508a5163d83d324160e01b81528a818e81865afa9081156124b3578e91612c53575b50612c4c91611032565b4210611f3a565b90508a81813d8311612c7f575b612c6a8183610de4565b81010312612c7b5751612c4c612c42565b8d80fd5b503d612c60565b90508981813d8311612cb0575b612c9d8183610de4565b81010312612cac57515f612c20565b8c80fd5b503d612c93565b90508981813d8311612ce5575b612cce8183610de4565b81010312612cac57612cdf9061108e565b5f611f33565b503d612cc4565b9092506060813d8211612d19575b81612d0760609383610de4565b81010312612b7057870151915f611f0e565b3d9150612cfa565b50505050509290918251956315e5a1e560e01b875284878381845afa968715612e10578397612de1575b50849084519283809263d83d324160e01b82525afa918215612dd6578092612da4575b5050612d9c907f9e8d991a48f5d9a58e5917bf48ca2ca14e9db9ee501bef971e22cb845afead099495611032565b9051908152a1565b9091508382813d8311612dcf575b612dbc8183610de4565b810103126105f057505184612d9c612d6e565b503d612db2565b8351903d90823e3d90fd5b9096508481813d8311612e09575b612df98183610de4565b810103126101d057519584612d4b565b503d612def565b84513d85823e3d90fd5b50821515611ed8565b5060ff60085416611ed2565b612e4891935060803d8111611798576117878183610de4565b505050915f611ec7565b612e699150873d89116103f4576103e68183610de4565b5f611e81565b60405163b4fa3fb360e01b8152600490fd5b919060408382031261091457612e968361108e565b602084015190936001600160401b03919082821161091457019082601f83011215610914578151908111610db55760405192612edc601f8301601f191660200185610de4565b8184526020828401011161091457610e4d9160208085019101610d31565b606090602081526002602082015261060f60f31b60408201520190565b15612f1e57565b60405162461bcd60e51b815260206004820152601c60248201527b13db9b1e4818d85b1b18589b1948189e481315931a591bd5985d5b1d60221b6044820152606490fd5b608090610e4d9392606082525f606083015260208201528160408201520190610d52565b5f924260095580156131ff57612f9c8383611032565b600182901b906001600160ff1b03831683036131eb57633b9aca00918281029281840414901517156131eb5790612fd291610e77565b92600754841080156131e0575b6130b7576001546001600160a01b0316803b15610405578580916024604051809481936369ea177160e01b83528a60048401525af180156130ac57613099575b5060ff19600854166008556040519260208401526040830152606082015260608152608081019080821060018060401b03831117610db557835f805160206132b683398151915293836040526060845261307c60e0840184610d52565b9060a08401528281039260c0607f19850191015252605f190190a2565b6130a590959195610da2565b935f61301f565b6040513d88823e3d90fd5b505060015460408051631627391760e11b81529093506001600160a01b039091169150602081600481855afa9081156131d3579084918291613192575b508351764352453a2052617465206f7574206f6620626f756e647360481b6020820152601781525f805160206132b68339815191529161313382610dc9565b613141865192839283612f62565b0390a2803b156101d05782809160248451809681936369ea177160e01b83528160048401525af1918215612a5c575050613183575b5060ff1960085416600855565b61318c90610da2565b5f613176565b9150506020813d82116131cb575b816131ad60209383610de4565b810103126102a5575183905f805160206132b68339815191526130f4565b3d91506131a0565b50505051903d90823e3d90fd5b506006548411612fdf565b634e487b7160e01b86526011600452602486fd5b505060015460408051631627391760e11b81529092506001600160a01b0390911690602081600481855afa9081156131d3579084918291613274575b508351704352453a204e6f2072617465206461746160781b6020820152601181525f805160206132b68339815191529161313382610dc9565b9150506020813d82116132ad575b8161328f60209383610de4565b810103126102a5575183905f805160206132b683398151915261323b565b3d915061328256fe06dc6048da4ff28ebed338bf9fc95ff051fa885b602dd9bb5b6852f34279adcfdeabea4a99c2720b845bb854b29be5c390c21b3e6188081b8092e68a37d6c9ee8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0a26469706673582212201d4622b52acbdfbdd376f162d5e592e61383dcf39c82664a5a671a0f533a332a64736f6c634300081400330000000000000000000000007727bbb88cddfedeccc65f454d129c795d4f8fce
Deployed Bytecode
0x6080604081815260049182361015610015575f80fd5b5f92833560e01c91826301ffc9a714610cb1575081630cc9c94d14610c925781632b1f03e914610c3b5781633070fbf814610c125781633a84701714610be95781634585e33b14610b7c5781634ed94b2414610b605781636e04ff0d14610b06578163715018a614610abf57816377bac89d14610a9657816378f42cc614610a72578163805f21321461077957816386c1b640146106e457816389506a44146106c85781638da5cb5b146106a0578163907f25791461067157816391ff95d1146106525781639bb1740514610630578163a298f6eb146104a4578163a314e4ae14610485578163b5334dfb14610409578163d777cc6d1461030e578163e5d6f8ce146101d4578163f2fde38b14610155575063f7dcf90414610135575f80fd5b346101515781600319360112610151576020905162093a808152f35b5080fd5b919050346101d05760203660031901126101d0576001600160a01b038235818116939192908490036101cc57610189610d77565b83156101b65750505f548260018060a01b03198216175f55165f805160206132f68339815191525f80a380f35b51631e4fbdf760e01b8152908101849052602490fd5b8480fd5b8280fd5b919050346101d057826003193601126101d0578051636e04ff0d60e01b8152838180610201868201612efa565b0381305afa90811561030457849085926102df575b50156102a9578390600a549260018060a01b0319943086861617600a55303b156102a5576020849161025f84519586938493634585e33b60e01b85528401526024830190610d52565b038183305af190811561029c5750610288575b505060018060a01b031690600a541617600a5580f35b61029190610da2565b6101d057825f610272565b513d84823e3d90fd5b8380fd5b815162461bcd60e51b8152602081850152601060248201526f139bc81d5c1ad9595c081b995959195960821b6044820152606490fd5b90506102fd91503d8086833e6102f58183610de4565b810190612e81565b905f610216565b82513d86823e3d90fd5b919050346101d05760203660031901126101d05781356001600160a01b0381811693848303610405578060208360015416865192838092638da5cb5b60e01b82525afa9081156103fb5761036e91849189916103cd575b50163314612f17565b84156103bf5750907f039ad854736757070884dd787ef1a7f58db33546639d1f3efddcf4a33fb8997e916103ab600a549451928392861683611018565b0390a16001600160a01b03191617600a5580f35b835163b4fa3fb360e01b8152fd5b6103ee915060203d81116103f4575b6103e68183610de4565b810190610fd7565b5f610365565b503d6103dc565b85513d89823e3d90fd5b8580fd5b9050346101d05760203660031901126101d0578035908115158092036102a5576001548351638da5cb5b60e01b81526001600160a01b039290916020918391829086165afa90811561047b5761046a93945085916103cd5750163314612f17565b60ff80196008541691161760085580f35b84513d87823e3d90fd5b5050346101515781600319360112610151576020906007549051908152f35b83833461015157602091826003193601126105f0575082548151633fabe5a360e21b80825260a095926001600160a01b039290879082908690829087165afa908115610626575f91610604575b5084516301afd7c160e11b815286818681737f39c581f595b53c5cb19bd0b3f8da6c935e2ca05afa9081156105fa5790889392915f916105bb575b5090670de0b6b3a764000061054561054b938835610e50565b04610e50565b92600554169385518095819382525afa9182156105b1576105769394955f9361057d575b5050610e77565b9051908152f35b61059d929350803d106105aa575b6105958183610de4565b810190610e1b565b505050905090858061056f565b503d61058b565b83513d5f823e3d90fd5b919293508782813d83116105f3575b6105d48183610de4565b810103126105f057505187929190670de0b6b3a764000061052c565b80fd5b503d6105ca565b86513d5f823e3d90fd5b61061b9150873d89116105aa576105958183610de4565b5050509050876104f1565b85513d5f823e3d90fd5b5050346101515781600319360112610151576020905166271471148780008152f35b5050346101515781600319360112610151576020906006549051908152f35b505034610151578160031936011261015157602090517330c5ef2997d6a882de52c4ec01b6d0a5e5b4faae8152f35b505034610151578160031936011261015157905490516001600160a01b039091168152602090f35b5050346101515781600319360112610151576020905160038152f35b9050346101d05760203660031901126101d05780356001600160a01b0381811693918490036101cc578260208260015416845192838092638da5cb5b60e01b82525afa90811561076f5790610743929187916103cd5750163314612f17565b8215610762575050600280546001600160a01b03191691909117905580f35b5163b4fa3fb360e01b8152fd5b83513d88823e3d90fd5b919050346101d057806003193601126101d0576001600160401b039180358381116101cc576107ab9036908301610d04565b93602435908111610405576107c39036908401610d04565b602092919295868210610a625787908483810103938885126101d0577f73135abe2f319d59aa80a5fc0d2fc64396089064a520a75cbd9338de75026b0782898b8260dd978b35978897855196858896875286015285850137828201840152601f01601f19168101030190a20361096e576080111561094f575050426009556001548251631627391760e11b81526001600160a01b03909116939081818481885afa908115610945575f91610918575b505f915f805160206132b683398151915291704352453a204e6f2072617465206461746160781b865192830152601182526108ac82610dc9565b6108ba865192839283612f62565b0390a2823b15610914575f6024819282855196879485936369ea177160e01b85528401525af190811561090b57506108fc575b5060ff19600854166008555b80f35b61090590610da2565b5f6108ed565b513d5f823e3d90fd5b5f80fd5b908282813d831161093e575b61092e8183610de4565b810103126105f05750515f610872565b503d610924565b84513d5f823e3d90fd5b6080919250126102a5576108f992606082013592820135910135612f86565b50508251636e04ff0d60e01b8152919392905084828061098f818801612efa565b0381305afa918215610a585785908693610a39575b506109b1575b5050505080f35b84600a549360018060a01b0319953087871617600a55303b156101d0576109ef84519586938493634585e33b60e01b85528401526024830190610d52565b038183305af1908115610a305750610a1d575b5060018060a01b031690600a541617600a555f8080806109aa565b610a2990939193610da2565b915f610a02565b513d86823e3d90fd5b9050610a509192503d8087833e6102f58183610de4565b91905f6109a4565b81513d87823e3d90fd5b855163b4fa3fb360e01b81528590fd5b50503461015157816003193601126101515760209060ff6008541690519015158152f35b50503461015157816003193601126101515760025490516001600160a01b039091168152602090f35b83346105f057806003193601126105f057610ad8610d77565b80546001600160a01b03198116825581906001600160a01b03165f805160206132f68339815191528280a380f35b9050346101d05760203660031901126101d05780356001600160401b0381116102a557610b3591369101610d04565b5050610b5c610b42611178565b839291925193849315158452806020850152830190610d52565b0390f35b5050346101515781600319360112610151576020905160088152f35b8383346101515760203660031901126101515782356001600160401b0381116101d057610bac9036908501610d04565b9160018060a01b03600a541633141580610bdf575b610bd05750906108f991611e28565b5163a73d374160e01b81528490fd5b5030331415610bc1565b50503461015157816003193601126101515760015490516001600160a01b039091168152602090f35b505034610151578160031936011261015157600a5490516001600160a01b039091168152602090f35b919050346101d05760603660031901126101d057600a546001600160a01b031633141580610c88575b610c7b57506108f990604435906024359035612f86565b5163a73d374160e01b8152fd5b5030331415610c64565b5050346101515781600319360112610151576020906009549051908152f35b8491346101d05760203660031901126101d0573563ffffffff60e01b81168091036101d0576020925063402f909960e11b8114908115610cf3575b5015158152f35b6301ffc9a760e01b14905083610cec565b9181601f84011215610914578235916001600160401b038311610914576020838186019501011161091457565b5f5b838110610d425750505f910152565b8181015183820152602001610d33565b90602091610d6b81518092818552858086019101610d31565b601f01601f1916010190565b5f546001600160a01b03163303610d8a57565b60405163118cdaa760e01b8152336004820152602490fd5b6001600160401b038111610db557604052565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b03821117610db557604052565b601f909101601f19168101906001600160401b03821190821017610db557604052565b51906001600160501b038216820361091457565b908160a091031261091457610e2f81610e07565b91602082015191604081015191610e4d608060608401519301610e07565b90565b81810292918115918404141715610e6357565b634e487b7160e01b5f52601160045260245ffd5b8115610e81570490565b634e487b7160e01b5f52601260045260245ffd5b6004805460408051633fabe5a360e21b80825291949360a0936001600160a01b039392918591839190829087165afa9081156105fa575f91610fb5575b5085516301afd7c160e11b8152602081600481737f39c581f595b53c5cb19bd0b3f8da6c935e2ca05afa908115610fab575f91610f7a575b50670de0b6b3a764000081810204809103610e63578491610f2a91610e50565b926005541691600487518094819382525afa92831561062657610e4d9495505f93610f56575050610e77565b610f6d929350803d106105aa576105958183610de4565b5050509050905f8061056f565b906020823d8211610fa3575b81610f9360209383610de4565b810103126105f05750515f610f0a565b3d9150610f86565b87513d5f823e3d90fd5b610fcc9150843d86116105aa576105958183610de4565b50505090505f610ed2565b9081602091031261091457516001600160a01b03811681036109145790565b9190826080910312610914578151916020810151916060604083015192015190565b6001600160a01b0391821681529116602082015260400190565b91908201809211610e6357565b81810392915f138015828513169184121617610e6357565b90670de0b6b3a764000091828102928184051490151715610e6357565b8115610e8157600160ff1b81145f19831416610e63570590565b5190811515820361091457565b8051156110a85760200190565b634e487b7160e01b5f52603260045260245ffd5b9060209081838203126109145782516001600160401b0393848211610914570181601f82011215610914578051938411610db5578360051b906040519461110585840187610de4565b85528380860192820101928311610914578301905b828210611128575050505090565b8151815290830190830161111a565b9081518082526020808093019301915f5b828110611156575050505090565b835185529381019392810192600101611148565b5f198114610e635760010190565b6001546040516316f0115b60e01b81526001600160a01b0390911691602082600481865afa801561141c57836080916111d7945f91611e09575b5060405163035d5e7f60e31b815294859283926001600160a01b031660048401611018565b03817330c5ef2997d6a882de52c4ec01b6d0a5e5b4faae5afa91821561141c575f92611de5575b5060ff6008541680611d24575b80611d1b575b611d005761121d610e95565b60405163c865d2a360e01b8082529190602081600481895afa90811561141c575f91611ccc575b50611252906112579261103f565b611057565b90604051908152602081600481885afa90811561141c575f91611c98575b5061127f91611074565b91604051636203c27d60e01b8152602081600481885afa90811561141c575f91611c66575b5060405163968dc49d60e01b815290602082600481895afa91821561141c575f92611c32575b506627147114878000828102048203610e63576112f0916627147114878000029061103f565b506040516375f4ed0f60e11b8152602081600481885afa90811561141c575f91611bf8575b5080611b37575b80611b2e575b156116385750604051636203c27d60e01b81525f9190602081600481885afa90811561162d5783916115fb575b5060405163968dc49d60e01b815293602085600481895afa9485156115f05784956115bc575b5092905b8312806115a9575b156114275761138f9061116a565b91604051636203c27d60e01b8152602081600481895afa90811561141c575f916113ea575b506113bf8486611032565b80662714711487800002906627147114878000820403610e63576113e29161103f565b909290611379565b906020823d602011611414575b8161140460209383610de4565b810103126105f05750515f6113b4565b3d91506113f7565b6040513d5f823e3d90fd5b6040516311420fdb60e11b80825292949350602081600481895afa90811561141c575f91611577575b508115159081611561575b81611557575b501561152a57508160030360038111610e635791600314611521575b60209060046040518096819382525afa92831561141c575f936114ed575b506114a69192610e77565b6114d0575b60405190602082016001600160401b03811183821017610db5576040525f82525f9190565b604051905f6020830152602082526114e782610dc9565b60019190565b6020813d602011611519575b8161150660209383610de4565b810103126102a5575192506114a661149b565b3d91506114f9565b6001915061147d565b600393945061153a929150611032565b106114ab576040519060036020830152602082526114e782610dc9565b905015155f611461565b9050600361156f8584611032565b11159061145b565b906020823d6020116115a1575b8161159160209383610de4565b810103126105f05750515f611450565b3d9150611584565b5060036115b68583611032565b10611381565b9094506020813d6020116115e8575b816115d860209383610de4565b810103126102a55751935f611375565b3d91506115cb565b6040513d86823e3d90fd5b90506020813d602011611625575b8161161660209383610de4565b810103126101d057515f61134f565b3d9150611609565b6040513d85823e3d90fd5b6040516375f4ed0f60e11b81529193925090602081600481865afa90811561141c575f91611af4575b5080611a33575b806119cd575b61167a575b50506114ab565b61169757506040519060026020830152602082526114e782610dc9565b60405163d59add2360e01b8152602081600481855afa90811561141c575f91611993575b506117be576040516316f0115b60e01b8152602081600481855afa91821561141c57611710926080925f9161179f575b5060405163035d5e7f60e31b815293849283926001600160a01b031660048401611018565b03817330c5ef2997d6a882de52c4ec01b6d0a5e5b4faae5afa90811561141c575f9161176d575b5015611755576040519060016020830152602082526114e782610dc9565b6040519060026020830152602082526114e782610dc9565b61178f915060803d608011611798575b6117878183610de4565b810190610ff6565b5050505f611737565b503d61177d565b6117b8915060203d6020116103f4576103e68183610de4565b5f6116eb565b604051632937571f60e11b815273889edc2edab5f40e902b864ad4d7ade8e412f9b1602082600481845afa91821561141c575f9261195f575b50600460206040519461180986610dc9565b600186528136818801376040516236b65760e11b815292839182905afa90811561141c575f9161192d575b5061183e8461109b565b525f604051808094633155f1fd60e11b8252606060048301526118646064830188611137565b906001602484015260448301520381845afa90811561141c576118c6935f93849361190e575b506118b4906040519586948593849363192f225b60e31b8552604060048601526044850190611137565b83810360031901602485015290611137565b03915afa801561141c576118e1915f916118ed575b5061109b565b51611755575f80611673565b611908913d8091833e6119008183610de4565b8101906110bc565b5f6118db565b611926906118b492943d8091833e6119008183610de4565b929061188a565b906020823d602011611957575b8161194760209383610de4565b810103126105f05750515f611834565b3d915061193a565b90916020823d60201161198b575b8161197a60209383610de4565b810103126105f0575051905f6117f7565b3d915061196d565b906020823d6020116119c5575b816119ad60209383610de4565b810103126105f057506119bf9061108e565b5f6116bb565b3d91506119a0565b5060405163e3ef710b60e01b8152602081600481865afa90811561141c575f916119f9575b501561166e565b906020823d602011611a2b575b81611a1360209383610de4565b810103126105f05750611a259061108e565b5f6119f2565b3d9150611a06565b506040516315e5a1e560e01b8152602081600481865afa90811561141c575f91611ac2575b5060405163d83d324160e01b8152602081600481875afa90811561141c575f91611a8e575b50611a8791611032565b4211611668565b906020823d602011611aba575b81611aa860209383610de4565b810103126105f0575051611a87611a7d565b3d9150611a9b565b906020823d602011611aec575b81611adc60209383610de4565b810103126105f05750515f611a58565b3d9150611acf565b906020823d602011611b26575b81611b0e60209383610de4565b810103126105f05750611b209061108e565b5f611661565b3d9150611b01565b50801515611322565b506040516315e5a1e560e01b8152602081600481885afa90811561141c575f91611bc6575b5060405163d83d324160e01b8152602081600481895afa90811561141c575f91611b92575b50611b8b91611032565b421061131c565b906020823d602011611bbe575b81611bac60209383610de4565b810103126105f0575051611b8b611b81565b3d9150611b9f565b906020823d602011611bf0575b81611be060209383610de4565b810103126105f05750515f611b5c565b3d9150611bd3565b906020823d602011611c2a575b81611c1260209383610de4565b810103126105f05750611c249061108e565b5f611315565b3d9150611c05565b90916020823d602011611c5e575b81611c4d60209383610de4565b810103126105f0575051905f6112ca565b3d9150611c40565b906020823d602011611c90575b81611c8060209383610de4565b810103126105f05750515f6112a4565b3d9150611c73565b906020823d602011611cc4575b81611cb260209383610de4565b810103126105f057505161127f611275565b3d9150611ca5565b906020823d602011611cf8575b81611ce660209383610de4565b810103126105f0575051611257611244565b3d9150611cd9565b9150506040519060dd6020830152602082526114e782610dc9565b50811515611211565b506040516315e5a1e560e01b8152602081600481875afa90811561141c575f91611db3575b5060405163d83d324160e01b8152602081600481885afa90811561141c575f91611d7f575b50611d7891611032565b421161120b565b906020823d602011611dab575b81611d9960209383610de4565b810103126105f0575051611d78611d6e565b3d9150611d8c565b906020823d602011611ddd575b81611dcd60209383610de4565b810103126105f05750515f611d49565b3d9150611dc0565b611dff91925060803d608011611798576117878183610de4565b505050905f6111fe565b611e22915060203d6020116103f4576103e68183610de4565b5f6111b2565b91905f908015612e6f578390602094859181010312610151573560018060a01b039360019085825416956040928351946316f0115b60e01b8652600495848188818d5afa90811561217357611ea0929185918a91612e52575b50169060808b88518095819263035d5e7f60e31b8352868d8401611018565b03817330c5ef2997d6a882de52c4ec01b6d0a5e5b4faae5afa928315612320578993612e2f575b5060dd811480612e23575b80612e1a575b612d2157869798999a60609697519687809463ca103d1560e01b8252838d8301526024998a915afa9283156121b1578b93612cec575b5088516375f4ed0f60e11b808252919089818d81855afa908115612206578d91612cb7575b5080612bfe575b80612bf5575b156125e8575050505050611f52610e95565b9382825416865163c865d2a360e01b9081815283818b81865afa9081156121b1578b916125b9575b50611252611f88918961103f565b885191825283828b81865afa9081156121b1578b91612588575b611fac9250611074565b8751636203c27d60e01b8082528b9392909185818d81875afa90811561220657908792918e91612557575b509092915b6123e6575b50505081151580612381575b8061232a575b1561226057508254875163968dc49d60e01b808252929186169084818c81855afa908115612256578c91612229575b5080600303600381116122175790600314612210575b89516311420fdb60e11b81529085828d81865afa8015612206578d906121d7575b6120639250610e77565b80612077575b505050505050505050505050565b813b156121d3578b90888c838d519586948593631f22aa2f60e01b85528401525af180156121b157908b916121bb575b50508484541692885192835280838b81875afa9081156121b1578b91612181575b506120d39250611032565b813b1561217d57889185839289519485938492631f49d99760e31b84528d8401525af180156121735790889161215b575b5050541693843b156104055790859291838551968794859363e7cdfb5d60e01b85528401525af190811561029c5750612147575b80808080808080808080612069565b6121518291610da2565b6105f05780612138565b61216490610da2565b61216f57865f612104565b8680fd5b86513d8a823e3d90fd5b8880fd5b905082813d83116121aa575b6121978183610de4565b81010312610914576120d391515f6120c8565b503d61218d565b89513d8d823e3d90fd5b6121c490610da2565b6121cf57895f6120a7565b8980fd5b8b80fd5b508582813d83116121ff575b6121ed8183610de4565b81010312610914576120639151612059565b503d6121e3565b8b513d8f823e3d90fd5b5084612038565b634e487b7160e01b8d5260118c52888dfd5b90508481813d831161224f575b6122408183610de4565b8101031261091457515f612022565b503d612236565b8a513d8e823e3d90fd5b979350919450855163968dc49d60e01b8152818185818b5afa9182156123205789926122f0575b505061229590600392611032565b10156122a4575b505050505050565b843b15610405579085929183855196879485936369e4921360e01b85528401525af190811561029c57506122dc575b8080808061229c565b6122e68291610da2565b6105f057806122d3565b90809250813d8311612319575b6123078183610de4565b81010312610914575181612295612287565b503d6122fd565b87513d8b823e3d90fd5b5087516311420fdb60e11b815283818b81855afa9081156121b1578b91612354575b501515611ff3565b90508381813d831161237a575b61236b8183610de4565b8101031261091457515f61234c565b503d612361565b50875163968dc49d60e01b815283818b81855afa9081156121b1578b916123b7575b506123b060039184611032565b1115611fed565b90508381813d83116123df575b6123ce8183610de4565b8101031261091457516123b06123a3565b503d6123c4565b8294919212806124f3575b156124ec576123ff9061116a565b92895182815285818d81875afa908115612206578d916124bf575b508a5163968dc49d60e01b815286818e81885afa80156124b35786908f90612482575b6124479250611032565b9066271471148780009180830292830403612470578792916124689161103f565b909291611fdc565b634e487b7160e01b8e5260118d52898efd5b50508681813d83116124ac575b6124998183610de4565b810103126109145785612447915161243d565b503d61248f565b8e8d51903d90823e3d90fd5b90508581813d83116124e5575b6124d68183610de4565b8101031261091457515f61241a565b503d6124cc565b9280611fe1565b50895163968dc49d60e01b815285818d81875afa908115612206578d91612528575b5061252260039183611032565b106123f1565b90508581813d8311612550575b61253f8183610de4565b810103126109145751612522612515565b503d612535565b809350878092503d8311612581575b6125708183610de4565b81010312610914578691515f611fd7565b503d612566565b90508382813d83116125b2575b61259f8183610de4565b8101031261091457611fac915190611fa2565b503d612595565b90508381813d83116125e1575b6125d08183610de4565b810103126109145751611252611f7a565b503d6125c6565b899a959b99989796985191825287828781845afa918215612bb257869189918c94612bbc575b5083612ae0575b83612ad2575b83612a6f575b505050612635575b50505050505050505050565b89811480612a66575b156129db5750505083875416938651631627391760e11b815284818481895afa9081156128fb5787916129ae575b5087516315e5a1e560e01b8152858185818a5afa908115612961578891612981575b5042039042821161296f5762093a80820180921161296f576301e13380916126b591610e50565b04946126bf610e95565b90803b1561296b57878091868b518094819363e7cdfb5d60e01b8352878a8401525af180156129615761294e575b507fcb505eb4841d5e2273dccaff5e53d23e263e5ca97c72458c2b68a9ca652b70dd858951838152a181895416958851634550079d60e01b8152868186818b5afa908115612944578991612917575b50670de0b6b3a7640000918201809211612905578892612760889361276593610e50565b610e77565b9660648a518094819363f12f845560e01b8352737f39c581f595b53c5cb19bd0b3f8da6c935e2ca08984015273889edc2edab5f40e902b864ad4d7ade8e412f9b18a8401528b60448401525af19081156128fb5787916128c6575b50156128915790838681949389519a6127d88c610dc9565b808c528336818e0137886127eb8d61109b565b525416926128108a519b8c968795869463324c1d6d60e21b8652850152830190611137565b03925af1928315612886579261284a575b5f805160206132d683398151915294508351928352820152a15b5f808080808080808080612629565b80925084813d831161287f575b6128618183610de4565b81010312610914575f805160206132d6833981519152935191612821565b503d612857565b8451903d90823e3d90fd5b5060116064928488519362461bcd60e51b85528401528201527020b8383937bb30b6103330b4b63ab9329760791b6044820152fd5b90508481813d83116128f4575b6128dd8183610de4565b8101031261216f576128ee9061108e565b5f6127c0565b503d6128d3565b88513d89823e3d90fd5b634e487b7160e01b8952601185528589fd5b90508681813d831161293d575b61292e8183610de4565b8101031261091457515f61273c565b503d612924565b8a513d8b823e3d90fd5b61295a90979197610da2565b955f6126ed565b89513d8a823e3d90fd5b8780fd5b634e487b7160e01b8852601184528488fd5b90508581813d83116129a7575b6129988183610de4565b8101031261296b57515f61268e565b503d61298e565b90508481813d83116129d4575b6129c58183610de4565b8101031261216f57515f61266c565b503d6129bb565b6002919394989796929550146129f9575b505050505050505061283b565b6002541690813b156101cc5791846044928195948851998a968795637aac8f3760e11b87528601528401525af1918215612a5c575050612a4d575b5060ff1960085416176008555f808080808080806129ec565b612a5690610da2565b5f612a34565b51903d90823e3d90fd5b5082151561263e565b8c5163e3ef710b60e01b815293509091839182905afa908115612944578991612a9d575b501584875f612621565b90508681813d8311612acb575b612ab48183610de4565b8101031261217d57612ac59061108e565b5f612a93565b503d612aaa565b60085460ff1615935061261b565b9250505089516315e5a1e560e01b815287818781855afa908115612bb2578a91612b85575b508a5163d83d324160e01b81529088828881865afa918215612b7b5791899188938d92612b40575b5090612b3891611032565b421192612615565b92839194508092503d8311612b74575b612b5a8183610de4565b81010312612b7057612b3887928a925191612b2d565b8a80fd5b503d612b50565b8c513d8d823e3d90fd5b90508781813d8311612bab575b612b9c8183610de4565b810103126121cf57515f612b05565b503d612b92565b8b513d8c823e3d90fd5b9250925081813d8311612bee575b612bd48183610de4565b810103126121cf5787612be7879261108e565b925f61260e565b503d612bca565b50841515611f40565b5089516315e5a1e560e01b815289818d81855afa908115612206578d91612c86575b508a5163d83d324160e01b81528a818e81865afa9081156124b3578e91612c53575b50612c4c91611032565b4210611f3a565b90508a81813d8311612c7f575b612c6a8183610de4565b81010312612c7b5751612c4c612c42565b8d80fd5b503d612c60565b90508981813d8311612cb0575b612c9d8183610de4565b81010312612cac57515f612c20565b8c80fd5b503d612c93565b90508981813d8311612ce5575b612cce8183610de4565b81010312612cac57612cdf9061108e565b5f611f33565b503d612cc4565b9092506060813d8211612d19575b81612d0760609383610de4565b81010312612b7057870151915f611f0e565b3d9150612cfa565b50505050509290918251956315e5a1e560e01b875284878381845afa968715612e10578397612de1575b50849084519283809263d83d324160e01b82525afa918215612dd6578092612da4575b5050612d9c907f9e8d991a48f5d9a58e5917bf48ca2ca14e9db9ee501bef971e22cb845afead099495611032565b9051908152a1565b9091508382813d8311612dcf575b612dbc8183610de4565b810103126105f057505184612d9c612d6e565b503d612db2565b8351903d90823e3d90fd5b9096508481813d8311612e09575b612df98183610de4565b810103126101d057519584612d4b565b503d612def565b84513d85823e3d90fd5b50821515611ed8565b5060ff60085416611ed2565b612e4891935060803d8111611798576117878183610de4565b505050915f611ec7565b612e699150873d89116103f4576103e68183610de4565b5f611e81565b60405163b4fa3fb360e01b8152600490fd5b919060408382031261091457612e968361108e565b602084015190936001600160401b03919082821161091457019082601f83011215610914578151908111610db55760405192612edc601f8301601f191660200185610de4565b8184526020828401011161091457610e4d9160208085019101610d31565b606090602081526002602082015261060f60f31b60408201520190565b15612f1e57565b60405162461bcd60e51b815260206004820152601c60248201527b13db9b1e4818d85b1b18589b1948189e481315931a591bd5985d5b1d60221b6044820152606490fd5b608090610e4d9392606082525f606083015260208201528160408201520190610d52565b5f924260095580156131ff57612f9c8383611032565b600182901b906001600160ff1b03831683036131eb57633b9aca00918281029281840414901517156131eb5790612fd291610e77565b92600754841080156131e0575b6130b7576001546001600160a01b0316803b15610405578580916024604051809481936369ea177160e01b83528a60048401525af180156130ac57613099575b5060ff19600854166008556040519260208401526040830152606082015260608152608081019080821060018060401b03831117610db557835f805160206132b683398151915293836040526060845261307c60e0840184610d52565b9060a08401528281039260c0607f19850191015252605f190190a2565b6130a590959195610da2565b935f61301f565b6040513d88823e3d90fd5b505060015460408051631627391760e11b81529093506001600160a01b039091169150602081600481855afa9081156131d3579084918291613192575b508351764352453a2052617465206f7574206f6620626f756e647360481b6020820152601781525f805160206132b68339815191529161313382610dc9565b613141865192839283612f62565b0390a2803b156101d05782809160248451809681936369ea177160e01b83528160048401525af1918215612a5c575050613183575b5060ff1960085416600855565b61318c90610da2565b5f613176565b9150506020813d82116131cb575b816131ad60209383610de4565b810103126102a5575183905f805160206132b68339815191526130f4565b3d91506131a0565b50505051903d90823e3d90fd5b506006548411612fdf565b634e487b7160e01b86526011600452602486fd5b505060015460408051631627391760e11b81529092506001600160a01b0390911690602081600481855afa9081156131d3579084918291613274575b508351704352453a204e6f2072617465206461746160781b6020820152601181525f805160206132b68339815191529161313382610dc9565b9150506020813d82116132ad575b8161328f60209383610de4565b810103126102a5575183905f805160206132b683398151915261323b565b3d915061328256fe06dc6048da4ff28ebed338bf9fc95ff051fa885b602dd9bb5b6852f34279adcfdeabea4a99c2720b845bb854b29be5c390c21b3e6188081b8092e68a37d6c9ee8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0a26469706673582212201d4622b52acbdfbdd376f162d5e592e61383dcf39c82664a5a671a0f533a332a64736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000007727bbb88cddfedeccc65f454d129c795d4f8fce
-----Decoded View---------------
Arg [0] : _LVLidoVault (address): 0x7727bBb88cDdfEDecCc65F454d129C795d4f8FcE
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000007727bbb88cddfedeccc65f454d129c795d4f8fce
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.