Overview
MON Balance
MON Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
AccountableFixedTerm
Compiler Version
v0.8.27+commit.40a35a09
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
import "../constants/Errors.sol";
import {IFeeManager} from "../interfaces/IFeeManager.sol";
import {IAccountableVault} from "../interfaces/IAccountableAsyncVault.sol";
import {IAccountableWithdrawalQueue} from "../interfaces/IAccountableWithdrawalQueue.sol";
import {
IAccountableFixedTerm,
InterestParams,
FixedTermInitParams,
InterestTotalsCache
} from "../interfaces/IAccountableFixedTerm.sol";
import {
IAccountableStrategy,
IStrategyVaultHooks,
IFeeManagerHooks,
IAccountableLoan,
LoanState,
LoanTerms
} from "../interfaces/IAccountableStrategy.sol";
import {AccountableStrategy} from "../strategies/AccountableStrategy.sol";
import {FixedTermStorage} from "../strategies/storage/FixedTermStorage.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
/// @title AccountableFixedTerm
/// @notice Implementation of a fixed term loan with interest payments
/// @dev Implements IAccountableFixedTerm interface and extends AccountableStrategy for managing loan lifecycle and payments
/// @custom:security-contact [email protected]
contract AccountableFixedTerm is UUPSUpgradeable, AccountableStrategy, FixedTermStorage {
using Math for uint256;
using SafeERC20 for IERC20;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
// ========================================================================== //
// Initialization //
// ========================================================================== //
function initialize(FixedTermInitParams memory params) external initializer {
__Pausable_init();
__UUPSUpgradeable_init();
if (params.asset == address(0)) revert ZeroAddress();
if (params.globals == address(0)) revert ZeroAddress();
if (params.feeManager == address(0)) revert ZeroAddress();
if (params.investmentManager == address(0)) revert ZeroAddress();
asset = params.asset;
globals = params.globals;
feeManager = params.feeManager;
loanState = LoanState.Initialized;
investmentManager = params.investmentManager;
penaltiesEnabled = true;
_makeVault(IERC20(params.asset), params.sharesTransferable, params.permissionLevel, params.name, params.symbol);
IERC20(params.asset).approve(vault, type(uint256).max);
IERC20(params.asset).approve(params.feeManager, type(uint256).max);
}
// ========================================================================== //
// Loan Terms Management //
// ========================================================================== //
/// @inheritdoc IAccountableLoan
function setTerms(LoanTerms memory terms) public onlyManager {
_requireLoanTermsNotSet();
_requireIntervalDurationPair(terms.duration, terms.interestInterval);
if (borrower == address(0)) revert ZeroAddress();
_setTerms(terms);
_updateInterestParams();
loanState = LoanState.TermsSet;
_loan.termsSetTime = block.timestamp;
emit LoanTermsSet(
terms.minCapacity,
terms.maxCapacity,
terms.interestRate,
terms.lateInterestPenalty,
terms.interestInterval,
terms.duration
);
}
/// @inheritdoc IAccountableLoan
/// @dev Capacity related values can be updated alongside action periods
/// @dev It does not consider interest rate, duration or interval changes
function updateTerms(LoanTerms memory terms) external onlyManager {
_requireLoanTermsSet();
if (terms.maxCapacity < _loan.maxCapacity) revert CapacityTooLow();
if (terms.acceptGracePeriod > _loan.acceptGracePeriod) revert AcceptGracePeriodTooLong();
_loan.maxCapacity = terms.maxCapacity;
_loan.minCapacity = terms.minCapacity;
_loan.minRedeem = terms.minRedeem;
_loan.minDeposit = terms.minDeposit;
_loan.depositPeriod = terms.depositPeriod;
_loan.acceptGracePeriod = terms.acceptGracePeriod;
_loan.termsUpdateTime = block.timestamp;
emit LoanTermsSet(
terms.minCapacity,
terms.maxCapacity,
_loan.interestRate,
_loan.lateInterestPenalty,
terms.interestInterval,
terms.duration
);
}
// ========================================================================== //
// Loan Lifecycle Functions //
// ========================================================================== //
/// @inheritdoc IAccountableLoan
/// @dev This should be callable when paused
/// It is safer to pause and then default to first prevent other operations
function defaultLoan() external onlySafetyModuleOrManager {
_requireLoanOngoing();
_defaultValidAt = block.timestamp + 1 days;
emit LoanDefaultInitiated();
}
/// @inheritdoc IAccountableLoan
/// @dev This should be callable when paused
/// Default coverage should be possible at all times if loan is in default
function coverDefault(uint256 assets) external onlySafetyModuleOrManager {
_requireLoanInDefault();
loanState = LoanState.InDefaultClaims;
IAccountableVault(vault).lockAssets(assets, msg.sender);
emit DefaultCovered(safetyModule, msg.sender, assets);
}
/// @inheritdoc IAccountableFixedTerm
function acceptLoanLocked() external onlyBorrower whenNotPaused {
_acceptLoan(LoanState.OngoingLocked);
}
/// @inheritdoc IAccountableFixedTerm
function acceptLoanDynamic() external onlyBorrower whenNotPaused {
_acceptLoan(LoanState.OngoingDynamic);
}
/// @dev Accepts the loan and updates the loan state
function _acceptLoan(LoanState state) private {
_requireLoanTermsSet();
_requireLoanNotOngoing();
if (!_minCapFilled()) revert LoanTermsNotMet();
_updateInterestParams();
loanState = state;
_loan.startTime = block.timestamp;
uint256 assets = _borrowable(msg.sender);
if (assets > 0) {
_updateAndRelease(assets);
}
emit LoanAccepted();
}
/// @inheritdoc IAccountableFixedTerm
function reject() external whenNotPaused {
_requireLoanNotOngoing();
uint256 depositDeadline = _loan.termsSetTime + _loan.depositPeriod;
uint256 acceptanceDeadline = depositDeadline + _loan.acceptGracePeriod;
if (block.timestamp <= acceptanceDeadline) revert LoanTermsNotMet();
loanState = LoanState.Rejected;
emit LoanRejected();
}
/// @inheritdoc IAccountableFixedTerm
function claimInterest() external whenNotPaused returns (uint256 claimedInterest) {
_requireLoanOngoing();
claimedInterest = _claimPending(msg.sender);
emit InterestClaimed(msg.sender, claimedInterest);
}
/// @dev Claims the pending interest for an account and updates the claimable interest
function _claimPending(address account) internal returns (uint256 claimedInterest) {
_settleAccount(account);
claimedInterest = _pendingInterest[account];
if (claimedInterest == 0) return 0;
_pendingInterest[account] = 0;
uint256 claimable = _loan.claimableInterest;
_loan.claimableInterest = claimable > claimedInterest ? (claimable - claimedInterest) : 0;
IAccountableVault(vault).releaseAssets(claimedInterest, account);
}
/// @inheritdoc IAccountableLoan
/// @dev Allows borrowing funds once loan is accepted and limited to the max borrowable amount
function borrow(uint256 assets) external onlyBorrower whenNotPaused {
_requireLoanOngoing();
uint256 borrowable = _borrowable(msg.sender);
uint256 maxBorrowable = Math.min(borrowable, assets);
if (maxBorrowable == 0) revert InsufficientAmount();
_updateAndRelease(maxBorrowable);
emit Borrowed(msg.sender, maxBorrowable);
}
/// @inheritdoc IAccountableLoan
function repay(uint256) external onlyBorrower whenNotPaused {
_requireLoanMatured();
_requireLoanOngoing();
if (_loan.outstandingInterest > 0) revert LoanCannotBeRepaid();
uint256 amount = _loan.outstandingPrincipal;
_loan.outstandingPrincipal = 0;
loanState = LoanState.Repaid;
_transferFrom(borrower, address(this), amount);
IAccountableVault(vault).lockAssets(amount, address(this));
emit LoanRepaid(amount);
}
/// @inheritdoc IAccountableLoan
function prepay() external onlyBorrower whenNotPaused {
_requireLoanOngoing();
uint256 currentInterval =
_currentInterval(_loan.startTime, _loan.interestInterval, _interestParams.numIntervals);
if (currentInterval > 0) {
if (_interestParams.lastPaymentInterval < currentInterval - 1) revert OutstandingInterestPayments();
if (currentInterval == 1 && _interestParams.lastPaymentInterval == 0) revert OutstandingInterestPayments();
}
uint256 amount = _loan.outstandingPrincipal;
uint256 prepayFee = IFeeManager(feeManager).prepaymentFee(address(this));
uint256 feeAmount = prepayFee > 0 ? amount.mulDiv(prepayFee, BASIS_POINTS) : 0;
_loan.outstandingPrincipal = 0;
_loan.outstandingInterest = 0;
_loan.drawableFunds = 0;
loanState = LoanState.Repaid;
uint256 totalAmount = amount + feeAmount;
_transferFrom(borrower, address(this), totalAmount);
IAccountableVault(vault).lockAssets(totalAmount, address(this));
emit LoanRepaid(amount);
}
/// @inheritdoc IAccountableLoan
function pay(uint256 amount) external onlyBorrower whenNotPaused {
_requireLoanOngoing();
uint256 interest;
uint256 penalties;
uint256 performanceFee;
uint256 establishmentFee;
uint256 currentInterval;
InterestTotalsCache memory ic = _interestTotalsCache();
(interest, penalties, performanceFee, establishmentFee, currentInterval) = _interestTotals(ic);
uint256 netInterest = (interest - performanceFee);
uint256 totalAmount = (interest + establishmentFee + penalties);
if (totalAmount == 0) revert NoPaymentDue();
if (amount < totalAmount) revert InsufficientAmount();
_interestParams.lastPaymentTime = block.timestamp;
_interestParams.lastPaymentInterval = currentInterval;
IAccountableVault vault_ = IAccountableVault(vault);
_accInterestPerShare += netInterest.mulDiv(PRECISION, vault_.totalSupply());
_loan.claimableInterest += netInterest;
_loan.outstandingInterest -= interest;
_transferFrom(borrower, address(this), totalAmount);
vault_.lockAssets(netInterest + penalties, address(this));
IFeeManager(feeManager).collect(asset, performanceFee, establishmentFee);
emit InterestPaid(msg.sender, totalAmount, penalties, performanceFee, establishmentFee, currentInterval);
}
// ========================================================================== //
// Hook Overrides //
// ========================================================================== //
/// @dev Overriden deposit hook with extra validation logic and balances update
function onDeposit(address share, uint256 assets, address receiver, address)
public
override(AccountableStrategy, IStrategyVaultHooks)
onlyVault
whenNotPaused
returns (uint256 price)
{
_requireCanRequestDeposit();
_requireMinDepositAmount(assets);
_requireBelowMaxCapacity(assets);
_loan.drawableFunds += assets;
_settleAccount(receiver);
price = _sharePrice(share);
}
/// @dev Overriden mint hook with extra validation logic and balances update
function onMint(address share, uint256 shares, address receiver, address)
public
override(AccountableStrategy, IStrategyVaultHooks)
onlyVault
whenNotPaused
returns (uint256 price)
{
_requireCanRequestDeposit();
price = _sharePrice(share);
uint256 assets = shares.mulDiv(price, PRECISION, Math.Rounding.Ceil);
_requireMinDepositAmount(assets);
_requireBelowMaxCapacity(assets);
_loan.drawableFunds += assets;
_settleAccount(receiver);
}
/// @dev Overriden onRequestRedeem hook
function onRequestRedeem(address share, uint256 shares, address, address owner)
public
override(AccountableStrategy, IStrategyVaultHooks)
onlyVault
whenNotPaused
returns (bool canFulfill, uint256 price)
{
_requireCanRequestRedeem();
if (_loan.minRedeem > shares) revert InsufficientShares();
canFulfill = true;
_claimPending(owner);
price = _sharePrice(share);
}
/// @dev Overriden onTransfer hook to settle interest accumulator checkpoint
function onTransfer(address, address from, address to, uint256)
public
override(AccountableStrategy, IStrategyVaultHooks)
onlyVault
whenNotPaused
{
_settleAccount(from);
_settleAccount(to);
}
/// @dev Settle one account's pending interest using current accumulator
function _settleAccount(address user) internal {
uint256 shares = IAccountableVault(vault).balanceOf(user);
uint256 accInterestPerShare_ = _accInterestPerShare;
if (shares == 0) {
_userIndex[user] = accInterestPerShare_;
return;
}
uint256 delta = accInterestPerShare_ - _userIndex[user];
if (delta == 0) return;
_pendingInterest[user] += (shares * delta) / PRECISION;
_userIndex[user] = accInterestPerShare_;
}
/// @dev Overriden onFeeStructureChange hook
function onFeeStructureChange() public override(AccountableStrategy, IFeeManagerHooks) {
super.onFeeStructureChange();
if (_loan.termsSetTime != 0) {
_updateInterestParams();
}
}
/// @dev Overriden maxDeposit hook
function maxDeposit(address, address)
public
view
override(AccountableStrategy, IStrategyVaultHooks)
returns (uint256 maxAssets)
{
maxAssets = _maxDeposit();
}
// ========================================================================== //
// View Functions //
// ========================================================================== //
/// @dev Private function for internal ease of use
function _maxDeposit() private view returns (uint256) {
return _loan.maxCapacity - _loan.outstandingPrincipal - _loan.drawableFunds;
}
/// @inheritdoc IAccountableLoan
function timeDelinquent() external view returns (uint256) {
uint256 gracePeriod_ = _loan.lateInterestGracePeriod;
uint256 dueInterval = _interestParams.lastPaymentInterval + 1;
uint256 prevUnpaidDueTime = _loan.startTime + (dueInterval * _loan.interestInterval);
if (block.timestamp > (prevUnpaidDueTime + gracePeriod_)) {
return (block.timestamp - (prevUnpaidDueTime + gracePeriod_)) / DAYS_1_SECONDS;
}
return 0;
}
/// @inheritdoc IAccountableFixedTerm
function interestData()
external
view
returns (
uint256 interest,
uint256 penalties,
uint256 performanceFee,
uint256 establishmentFee,
uint256 currentInterval
)
{
InterestTotalsCache memory ic = _interestTotalsCache();
if (ic.startTime == 0) return (0, 0, 0, 0, 0);
(interest, penalties, performanceFee, establishmentFee, currentInterval) = _interestTotals(ic);
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
return interfaceId == type(IAccountableLoan).interfaceId
|| interfaceId == type(IAccountableStrategy).interfaceId
|| interfaceId == type(IAccountableFixedTerm).interfaceId || interfaceId == type(IERC165).interfaceId;
}
// ========================================================================== //
// Internal Helper Functions //
// ========================================================================== //
/// @dev Wrapper function for IERC20 safeTransferFrom
function _transferFrom(address from, address to, uint256 amount) private {
IERC20(asset).safeTransferFrom(from, to, amount);
}
/// @dev Caches the interest calculation parameters
/// This is called when the loan is accepted and terms are final
function _updateInterestParams() private {
uint256 duration = _loan.duration;
uint256 interval = _loan.interestInterval;
uint256 interestRate = _loan.interestRate;
uint256 feeFactor = BASIS_POINTS - IFeeManager(feeManager).performanceFee(address(this));
uint256 rateWithoutFee = interestRate.mulDiv(feeFactor, BASIS_POINTS);
uint256 durationInYears = duration.mulDiv(PRECISION, DAYS_365_SECONDS);
uint256 intervalInYears = interval.mulDiv(PRECISION, DAYS_365_SECONDS);
uint256 lastPaymentInterval = _interestParams.lastPaymentInterval;
uint256 lastPaymentTime = _interestParams.lastPaymentTime;
_interestParams = InterestParams({
intervalRate: interestRate.mulDiv(intervalInYears, BASIS_POINTS), // precision decimals
netReturn: rateWithoutFee.mulDiv(durationInYears, BASIS_POINTS), // share decimals
numIntervals: duration / interval, // no decimal precision
lastPaymentInterval: lastPaymentInterval,
lastPaymentTime: lastPaymentTime
});
}
/// @dev Updates the loan state and releases the assets to the borrower
function _updateAndRelease(uint256 assets) private {
_loan.drawableFunds -= assets;
_loan.outstandingPrincipal += assets;
uint256 startTime = _loan.startTime;
uint256 interval = _loan.interestInterval;
uint256 numIntervals = _interestParams.numIntervals;
uint256 intervalRate = _interestParams.intervalRate;
uint256 currInterval = _currentInterval(startTime, interval, numIntervals);
_loan.outstandingInterest += _remainingInterest(assets, currInterval, intervalRate, numIntervals);
IAccountableVault(vault).releaseAssets(assets, msg.sender);
}
/// @dev Calculate the share price at current moment, using the virtual balances of the loan
/// Share price required to use decimals precision stores as PRECION in the vault
/// @return The share price
function _sharePrice(address) internal view override returns (uint256) {
uint256 one = PRECISION;
LoanState loanState_ = loanState;
// If deposits are made during the deposit period, share price is 1:1
if (_isInDepositPeriod() || loanState_ == LoanState.Rejected) {
return one;
}
// If loan is repaid the share price reflects available assets relative to shares
if (loanState_ == LoanState.Repaid || loanState_ == LoanState.InDefaultClaims) {
return IAccountableVault(vault).assetShareRatio();
}
// After grace period, the share price reflects the virtual interest accrued
// The share price will reflect the linear interest accrual every interval
uint256 netReturn = _interestParams.netReturn;
uint256 numIntervals = _interestParams.numIntervals;
uint256 nextInterval = _nextInterval(_loan.startTime, _loan.interestInterval, numIntervals);
// If the next interval is the last interval the price is the max net return
if (nextInterval >= numIntervals) {
return one + netReturn;
}
uint256 finalReturn = one + netReturn;
uint256 remainingIntervals = numIntervals - nextInterval;
uint256 potentialReturn = one + netReturn.mulDiv(remainingIntervals, numIntervals);
// Calculate share price: sp = (1 + r_max) / (1 + r_potential)
return finalReturn.mulDiv(one, potentialReturn);
}
/// @dev Returns the total amount of reserved accrued assets
function _accruedAssets(address) internal view override returns (uint256) {
return _loan.claimableInterest;
}
/// @dev Returns the total amount of drawable assets
function _borrowable(address) internal view returns (uint256) {
uint256 drawableFunds = _loan.drawableFunds;
uint256 reservedLiquidity = IAccountableWithdrawalQueue(vault).reservedLiquidity();
return drawableFunds > reservedLiquidity ? drawableFunds - reservedLiquidity : 0;
}
/// @dev Calculates the current interest payment related values
function _interestTotals(InterestTotalsCache memory ic)
private
view
returns (
uint256 interest,
uint256 penalties,
uint256 performanceFee,
uint256 establishmentFee,
uint256 currentInterval
)
{
(uint256 intervalsElapsed, bool isLast) = _intervalsSincePayment(
block.timestamp, ic.lastPaidTime, ic.startTime, ic.lastPaidInterval, ic.interval, ic.maxIntervals
);
interest =
isLast ? _loan.outstandingInterest : _calculateInterest(ic.principal, ic.intervalRate, intervalsElapsed);
address feeManager_ = feeManager;
{
uint256 performanceRate = IFeeManager(feeManager_).performanceFee(address(this));
performanceFee = _performanceFee(interest, performanceRate);
}
{
uint256 establishmentRate = IFeeManager(feeManager_).establishmentFee(address(this));
uint256 principal = ic.principal;
uint256 maxIntervals = ic.maxIntervals;
uint256 duration = ic.duration;
establishmentFee = _prorataFee(principal, establishmentRate, intervalsElapsed, maxIntervals, duration);
}
currentInterval = _currentInterval(ic.startTime, ic.interval, ic.maxIntervals);
penalties = _interestPenalties(interest, block.timestamp, currentInterval, ic);
}
/// @dev Calculates the prorated fee for an amount given the elapsed intervals
function _prorataFee(uint256 amount, uint256 feeRate, uint256 intervals, uint256 maxIntervals, uint256 duration)
internal
pure
returns (uint256)
{
if (feeRate == 0 || intervals == 0) return 0;
uint256 numerator = amount * feeRate * duration * intervals;
uint256 denominator = DAYS_365_SECONDS * BASIS_POINTS * maxIntervals;
return Math.mulDiv(numerator, 1, denominator, Math.Rounding.Ceil);
}
/// @dev Calculates the performance fee for the amount being paid
function _performanceFee(uint256 amount, uint256 feeRate) private pure returns (uint256) {
return amount.mulDiv(feeRate, BASIS_POINTS);
}
/// @dev Calculate the total interest owed for the drawn amount in the remaining duration of the loan
function _remainingInterest(uint256 amount, uint256 currentInterval, uint256 intervalRate, uint256 maxIntervals)
internal
pure
returns (uint256)
{
uint256 remainingIntervals = maxIntervals - currentInterval;
return amount.mulDiv(remainingIntervals * intervalRate, PRECISION);
}
/// @dev Calculates the interest for the amount for a number of elapsed intervals
function _calculateInterest(uint256 amount, uint256 interestRate, uint256 intervals)
private
pure
returns (uint256)
{
return amount.mulDiv(interestRate * intervals, PRECISION);
}
/// @dev Returns the number of intervals elapsed since last payment
/// Bound by max number of intervals of the current loan terms
/// Does not check for loan status which can lead to incorrect results due to startTime not being set
function _intervalsSincePayment(
uint256 timestamp,
uint256 lastTime,
uint256 startTime,
uint256 lastPaidInterval,
uint256 interestInterval,
uint256 maxIntervals
) private pure returns (uint256 intervals, bool isLast) {
uint256 rawElapsed;
uint256 maxElapsed;
if (lastTime != 0) {
if (timestamp <= lastTime) return (0, false);
rawElapsed = (timestamp - lastTime) / interestInterval;
maxElapsed = maxIntervals - lastPaidInterval;
} else {
if (timestamp <= startTime) return (0, false);
rawElapsed = (timestamp - startTime) / interestInterval;
maxElapsed = maxIntervals;
}
(intervals, isLast) = rawElapsed >= maxElapsed ? (maxElapsed, true) : (rawElapsed, false);
}
/// @dev Calculates the interest penalties at current loan state
function _interestPenalties(
uint256 amount,
uint256 timestamp,
uint256 currentInterval,
InterestTotalsCache memory ic
) private pure returns (uint256) {
if (!ic.penaltiesEnabled) return 0;
if (currentInterval <= ic.lastPaidInterval) return 0;
uint256 daysOverdue;
uint256 prevUnpaidDueTime = ic.startTime + ((ic.lastPaidInterval + 1) * ic.interval);
if (timestamp > (prevUnpaidDueTime + ic.lateGracePeriod)) {
daysOverdue = (timestamp - (prevUnpaidDueTime + ic.lateGracePeriod)) / DAYS_1_SECONDS;
}
return daysOverdue > 0 ? amount.mulDiv(daysOverdue * ic.lateInterestPenalty, BASIS_POINTS) : 0;
}
/// @dev Returns the payment interval at a given timestamp
/// Bound by max number of intervals of the current loan
function _currentInterval(uint256 startTime, uint256 interestInterval, uint256 maxIntervals)
private
view
returns (uint256)
{
if (startTime == 0) return 0;
uint256 interval = ((block.timestamp - startTime) / interestInterval);
return interval > maxIntervals ? maxIntervals : interval;
}
/// @dev Returns the next interval at a given timestamp
/// Bound by max number of intervals of the current loan terms
function _nextInterval(uint256 startTime, uint256 interestInterval, uint256 maxIntervals)
private
view
returns (uint256)
{
return _currentInterval(startTime, interestInterval, maxIntervals) + 1;
}
/// @dev Aggregate storage variables and return as a cache struct
function _interestTotalsCache() internal view returns (InterestTotalsCache memory) {
return InterestTotalsCache({
duration: _loan.duration,
startTime: _loan.startTime,
interval: _loan.interestInterval,
principal: _loan.outstandingPrincipal,
lateGracePeriod: _loan.lateInterestGracePeriod,
lateInterestPenalty: _loan.lateInterestPenalty,
maxIntervals: _interestParams.numIntervals,
intervalRate: _interestParams.intervalRate,
lastPaidTime: _interestParams.lastPaymentTime,
lastPaidInterval: _interestParams.lastPaymentInterval,
penaltiesEnabled: penaltiesEnabled
});
}
/// @dev Checks that the duration and interest interval are a valid pair
function _requireIntervalDurationPair(uint256 duration, uint256 interestInterval) private pure {
if (interestInterval == 0) revert InvalidIntervalLength();
if (duration < interestInterval) revert InvalidIntervalLength();
if (duration % interestInterval != 0) revert InvalidIntervalDurationPair();
}
/// @dev Checks that the loan is matured
function _requireLoanMatured() internal view {
if (block.timestamp < _loan.startTime + _loan.duration && loanState != LoanState.Repaid) {
revert LoanNotMatured();
}
}
/// @dev Checks that deposits are allowed
function _requireCanRequestDeposit() internal view {
if (loanState != LoanState.TermsSet && loanState != LoanState.OngoingDynamic) {
revert DepositNotAllowed();
}
}
/// @dev Checks that the deposit amount is below the max capacity
function _requireBelowMaxCapacity(uint256 assets) internal view {
if (assets > _maxDeposit()) revert ExceedsMaxDeposit();
}
/// @dev Checks that redeem requests are allowed
/// It allows redemption requests in the following states:
/// [InDefaultClaims] || [Repaid] || [Rejected]
function _requireCanRequestRedeem() internal view {
if (loanState != LoanState.InDefaultClaims && loanState != LoanState.Repaid && loanState != LoanState.Rejected)
{
revert RedeemNotAllowed();
}
}
/// @dev Returns whether the min cap has been filled
function _minCapFilled() internal view returns (bool) {
return (_loan.outstandingPrincipal + _loan.drawableFunds) >= _loan.minCapacity;
}
/// @dev Checks whether the current timestamp is within the deposit period
function _isInDepositPeriod() internal view returns (bool) {
LoanState loanState_ = loanState;
return block.timestamp <= _loan.termsSetTime + _loan.depositPeriod
&& (loanState_ != LoanState.OngoingLocked && loanState_ != LoanState.OngoingDynamic);
}
/// @dev Hook for UUPS upgradeable contract
function _authorizeUpgrade(address newImplementation) internal override onlyManagerOrSecurityAdmin {}
}// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.20; // ========================================================================== // // Authorization Errors // // ========================================================================== // /// @notice Error thrown when unauthorized error Unauthorized(); /// @notice Thrown when a signature verification fails due to invalid signer error InvalidSigner(); /// @notice Thrown when an operation is attempted with invalid operator permissions error InvalidOperator(); /// @notice Error thrown when invalid pending borrower is provided error InvalidPendingBorrower(); /// @notice Thrown when KYC status is not verified error KYCNotVerified(); /// @notice Thrown when a block expiration is invalid (e.g. in the past) error InvalidBlockExpiration(); /// @notice Thrown when a signature verification fails error InvalidSignature(); /// @notice Thrown when a message data length is invalid error InvalidMsgDataLength(); /// @notice Thrown when an accounts array is empty error EmptyAccountsArray(); // ========================================================================== // // Vault operations errors // // ========================================================================== // /// @notice Error thrown when trying to cancel a deposit request failed error CancelDepositRequestFailed(); /// @notice Error thrown when trying to cancel a redeem request failed error CancelRedeemRequestFailed(); /// @notice Error thrown when deposit amount exceeds max deposit error ExceedsMaxDeposit(); /// @notice Error thrown when assets exceed redemption limits error ExceedsRedeemLimit(); /// @notice Error thrown when redeem amount exceeds max redeem error ExceedsMaxRedeem(); /// @notice Error thrown when deposit/withdraw amount is insufficient error InsufficientAmount(); /// @notice Error thrown when redeem share amount is insufficient error InsufficientShares(); /// @notice Error thrown when no redeem request exists error NoRedeemRequest(); /// @notice Error thrown when no pending redeem request exists error NoPendingRedeemRequest(); /// @notice Error thrown when no cancel redeem request exists error NoCancelRedeemRequest(); /// @notice Thrown when attempting to transfer shares that are not transferable error SharesNotTransferable(); /// @notice Error thrown when no queue request exists error NoQueueRequest(); /// @notice Error thrown when redeem amount exceeds liquidity error InsufficientLiquidity(); /// @notice Error thrown when no queue requests exist error NoQueueRequests(); /// @notice Error thrown when asset is not whitelisted error AssetNotWhitelisted(); // ========================================================================== // // Loan operations errors // // ========================================================================== // /// @notice Error thrown when loan terms are already set error LoanTermsAlreadySet(); /// @notice Error thrown when loan terms are not set error LoanTermsNotSet(); /// @notice Error thrown when loan is ongoing error LoanOngoing(); /// @notice Error thrown when loan is not ongoing error LoanNotOngoing(); /// @notice Error thrown when loan is not in default error LoanNotInDefault(); /// @notice Error thrown when loan terms are not met error LoanTermsNotMet(); /// @notice Error thrown when loan has not matured error LoanNotMatured(); /// @notice Error thrown when repayment terms are not met error LoanCannotBeRepaid(); /// @notice Error thrown when interest is already claimed error InterestAlreadyClaimed(); /// @notice Error thrown when there are outstanding interest payments error OutstandingInterestPayments(); /// @notice Thrown when an operation is attempted with invalid controller permissions error InvalidController(); /// @notice Thrown when an operation is attempted with invalid vault manager permissions error InvalidVaultManager(); /// @notice Thrown when an operation is attempted with invalid interval duration pair error InvalidIntervalDurationPair(); /// @notice Thrown when an operation is attempted with invalid interval length error InvalidIntervalLength(); /// @notice Thrown when an operation is attempted with invalid withdrawal period error InvalidWithdrawalPeriod(); /// @notice Error thrown when capacity is set to a lower value than acceptable error CapacityTooLow(); /// @notice Error thrown when accept grace period is longer than acceptable for the loan error AcceptGracePeriodTooLong(); /// @notice Error thrown when a threshold exceeds the max bound error ThresholdTooHigh(); /// @notice Error thrown when attempting to pay when no payment is due error NoPaymentDue(); // ========================================================================== // // General errors // // ========================================================================== // /// @notice Thrown when an index is out of bounds error OutOfBounds(); /// @notice Thrown when an invalid range is provided error InvalidRange(); /// @notice Thrown when an operation is requested with zero amount error ZeroAmount(); /// @notice Thrown when an input address is address(0) error ZeroAddress(); /// @notice Thrown when deployment of a loan proxy contract fails /// @param reason The reason for the failed deployment error FailedDeployment(string reason); /// @notice Thrown when a deposit is not allowed error DepositNotAllowed(); /// @notice Thrown when a redeem is not allowed error RedeemNotAllowed(); /// @notice Thrown when input arrays lengths don't match error ArrayLengthMismatch(); /// @notice Error thrown when an operation is not required/allowed for the current permission level error PermissionLevelMismatch(); /// @notice Error thrown when an operation is not supported by the strategy error NotSupportedByStrategy(); // ========================================================================== // // Fee Manager errors // // ========================================================================== // /// @notice Error thrown when manager split is invalid error InvalidManagerSplit(); /// @notice Error thrown when performance fee is invalid error InvalidPerformanceFee(); /// @notice Error thrown when establishment fee is invalid error InvalidEstablishmentFee(); // ========================================================================== // // Rewards errors // // ========================================================================== // /// @notice Error thrown when a root is already set error RootAlreadySet(); /// @notice Error thrown when no root is set error RootNotSet(); /// @notice Error thrown when a root is already pending error RootAlreadyPending(); /// @notice Error thrown when no pending root is set error NoPendingRoot(); /// @notice Error thrown when timelock is not expired error TimelockNotExpired(); /// @notice Error thrown when a proof is invalid error InvalidProof(); /// @notice Error thrown when the amount to claim is not enough error NotEnoughClaimableAmount(); /// @notice Error thrown when an invalid rewards type is provided error InvalidRewardsType();
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
import {IRecoverable} from "./IRecoverable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
/// @notice The fee structure for a strategy
struct FeeStructure {
/// @notice The establishment fee
uint256 establishmentFee;
/// @notice The performance fee
uint256 performanceFee;
/// @notice The manager split
uint256 managerSplit;
/// @notice Whether to override the default fees
bool overrideDefaults;
}
interface IFeeManager is IRecoverable, IERC165 {
/// @notice An event that is emitted when fees are collected
event Collected(address indexed asset, address indexed strategy, uint256 performanceFee, uint256 establishmentFee);
/// @notice An event that is emitted when protocol fees are withdrawn
event Withdraw(address indexed asset, address indexed strategy, address indexed receiver, uint256 amount);
/// @notice An event that is emitted when the fee structure is set
event FeeStructureSet(address indexed strategy, FeeStructure feeStructure);
/// @notice An event that is emitted when the pending fee structure is set
event PendingFeeStructureSet(address indexed strategy, FeeStructure feeStructure);
/// @notice An event that is emitted when the performance fee is set
event PerformanceFeeSet(address indexed strategy, uint256 performanceFee);
/// @notice An event that is emitted when the prepayment fee is set
event PrepaymentFeeSet(address indexed strategy, uint256 prepaymentFee);
/// @notice An event that is emitted when the treasury is set
event TreasurySet(address indexed oldTreasury, address indexed newTreasury);
/// @notice Collects strategy fees
/// @param asset The address of the asset
/// @param performanceFee The performance fee
/// @param establishmentFee The establishment fee
function collect(address asset, uint256 performanceFee, uint256 establishmentFee) external;
/// @notice Collects establishment fees
/// @param asset The address of the asset
/// @param amount The amount of establishment fees
function collectEstablishmentFee(address asset, uint256 amount) external;
/// @notice Collects performance fees
/// @param asset The address of the asset
/// @param amount The amount of performance fees
function collectPerformanceFee(address asset, uint256 amount) external;
/// @notice Approves the pending fee structure
/// @param strategy The address of the strategy
function approvePendingFeeStructure(address strategy) external;
/// @notice Withdraws protocol fees
/// @param asset The address of the asset
function withdrawProtocolFee(address asset) external;
/// @notice Withdraws manager fees
/// @param receiver The address of the receiver
/// @param asset The address of the asset
/// @param strategy The address of the strategy
function withdrawManagerFee(address receiver, address asset, address strategy) external;
/// @notice Sets the fee structure
/// @param strategy The address of the strategy
/// @param feeStructure The fee structure
function setFeeStructure(address strategy, FeeStructure memory feeStructure) external;
/// @notice Sets the pending fee structure
/// @param strategy The address of the strategy
/// @param feeStructure The fee structure
function setPendingFeeStructure(address strategy, FeeStructure memory feeStructure) external;
/// @notice Sets the performance fee
/// @param strategy The address of the strategy
/// @param performanceFee The performance fee
function setPerformanceFee(address strategy, uint256 performanceFee) external;
/// @notice Sets the prepayment fee
/// @param strategy The address of the strategy
/// @param prepaymentFee The prepayment fee
function setPrepaymentFee(address strategy, uint256 prepaymentFee) external;
/// @notice Sets the treasury
/// @param treasury The address of the treasury
function setTreasury(address treasury) external;
/// @notice Returns the establishment fee
/// @param strategy The address of the strategy
/// @return The establishment fee
function establishmentFee(address strategy) external view returns (uint256);
/// @notice Returns the performance fee
/// @param strategy The address of the strategy
/// @return The performance fee
function performanceFee(address strategy) external view returns (uint256);
/// @notice Returns the manager split
/// @param strategy The address of the strategy
/// @return The manager split
function managerSplit(address strategy) external view returns (uint256);
/// @notice Returns the protocol split
/// @param strategy The address of the strategy
/// @return The protocol split
function protocolSplit(address strategy) external view returns (uint256);
/// @notice Returns the prepayment fee
/// @param strategy The address of the strategy
/// @return The prepayment fee
function prepaymentFee(address strategy) external view returns (uint256);
/// @notice Returns the fee structure
/// @param strategy The address of the strategy
/// @return The fees percentages for the strategy
function feeStructure(address strategy) external view returns (FeeStructure memory);
/// @notice Returns the pending fee structure
/// @param strategy The address of the strategy
/// @return The pending fee structure
function pendingFeeStructure(address strategy) external view returns (FeeStructure memory);
/// @notice Returns the treasury
/// @return The treasury
function treasury() external view returns (address);
/// @notice Returns the manager fees
/// @param manager The address of the manager
/// @param asset The address of the asset
/// @param strategy The address of the strategy
/// @return The manager fees of a given strategy
function managerFees(address manager, address asset, address strategy) external view returns (uint256);
/// @notice Returns the total manager fees
/// @param manager The address of the manager
/// @param asset The address of the asset
/// @param strategies The addresses of the strategies
/// @return The total manager fees of a given asset and strategies
function managerTotalFees(address manager, address asset, address[] calldata strategies)
external
view
returns (uint256);
/// @notice Returns the protocol fees
/// @param asset The address of the asset
/// @return The protocol fees
function protocolFees(address asset) external view returns (uint256);
/// @notice The basis points for the fee manager
/// @return The basis points
function BASIS_POINTS() external pure returns (uint256);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
/// @notice Struct tracking vault state for a controller
struct VaultState {
/// @notice Maximum shares that can be minted
uint256 maxMint;
/// @notice Maximum assets that can be withdrawn
uint256 maxWithdraw;
/// @notice Total accumulated assets unclaimed
uint256 depositAssets;
/// @notice Total accumulated shares unclaimed
uint256 redeemShares;
/// @notice Share price floored for deposit
uint256 depositPrice;
/// @notice Share price ceiled for mint
uint256 mintPrice;
/// @notice Share price floored for redeem
uint256 redeemPrice;
/// @notice Share price ceiled for withdraw
uint256 withdrawPrice;
/// @notice Pending deposit amount
uint256 pendingDepositRequest;
/// @notice Pending redeem amount
uint256 pendingRedeemRequest;
}
interface IControllerOperator {
/// @notice Emitted when an operator's approval status is updated
/// @param controller The controller address that approved/revoked
/// @param operator The operator address being approved/revoked
/// @param approved The new approval status
event OperatorSet(address indexed controller, address indexed operator, bool approved);
/// @notice Sets or revokes operator approval for a caller
/// @param operator The address to set approval for
/// @param approved True to approve, false to revoke
/// @return bool True if successful
function setOperator(address operator, bool approved) external returns (bool);
/// @notice Checks if an address is an approved operator for a controller
/// @param controller The controller address to check
/// @param operator The operator address to check
/// @return status True if operator is approved for controller
function isOperator(address controller, address operator) external view returns (bool status);
}
interface IAsyncDeposit {
/// @notice Emitted when a deposit request is created
/// @param controller The controller address for the deposit
/// @param owner The owner address that will receive shares
/// @param requestId The unique ID for tracking the request
/// @param sender The address that initiated the request
/// @param assets The amount of assets being deposited
event DepositRequest(
address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets
);
/// @notice Requests a deposit of assets
/// @param assets Amount of assets to deposit
/// @param controller Address controlling the deposit
/// @param owner Address that will own the shares
/// @return uint256 Request ID for tracking the deposit
function requestDeposit(uint256 assets, address controller, address owner) external returns (uint256);
/// @notice Gets pending deposit amount for a request
/// @param requestId ID of the deposit request
/// @param controller Controller address for the request
/// @return uint256 Amount of assets pending deposit
function pendingDepositRequest(uint256 requestId, address controller) external view returns (uint256);
/// @notice Gets claimable deposit amount for a request
/// @param requestId ID of the deposit request
/// @param controller Controller address for the request
/// @return uint256 Amount of assets claimable
function claimableDepositRequest(uint256 requestId, address controller) external view returns (uint256);
}
interface IAsyncRedeem {
/// @notice Emitted when a redeem request is created
/// @param controller The controller address for the redemption
/// @param owner The owner of the shares being redeemed
/// @param requestId The unique ID for tracking the request
/// @param sender The address that initiated the request
/// @param assets The amount of assets being redeemed
event RedeemRequest(
address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets
);
/// @notice Requests redemption of shares
/// @param shares Amount of shares to redeem
/// @param controller Controller address for redemption
/// @param owner Owner of the shares
/// @return uint256 Request ID for tracking redemption
function requestRedeem(uint256 shares, address controller, address owner) external returns (uint256);
/// @notice Gets pending redeem amount for a request
/// @param requestId ID of the redeem request
/// @param controller Controller address for the request
/// @return uint256 Amount of shares pending redemption
function pendingRedeemRequest(uint256 requestId, address controller) external view returns (uint256);
/// @notice Gets claimable redeem amount for a request
/// @param requestId ID of the redeem request
/// @param controller Controller address for the request
/// @return uint256 Amount of shares claimable
function claimableRedeemRequest(uint256 requestId, address controller) external view returns (uint256);
}
interface IAsyncCancelDeposit {
/// @notice Emitted when a deposit request is cancelled
/// @param controller The controller address for the deposit
/// @param receiver The address that received the cancelled deposit
/// @param requestId The ID of the cancelled request
/// @param sender The address that initiated the cancellation
event CancelDepositRequest(address indexed controller, address indexed receiver, uint256 requestId, address sender);
/// @notice Cancels a pending deposit request
/// @param controller Controller address for the request
/// @param receiver The address that received the cancelled deposit
function cancelDepositRequest(address controller, address receiver) external;
}
interface IAsyncCancelRedeem {
/// @notice Emitted when a redeem request is cancelled
/// @param controller The controller address for the redemption
/// @param receiver The address that received the cancelled redeem
/// @param requestId The ID of the cancelled request
/// @param sender The address that initiated the cancellation
event CancelRedeemRequest(address indexed controller, address indexed receiver, uint256 requestId, address sender);
/// @notice Cancels a pending redeem request
/// @param controller Controller address for the request
/// @param receiver The address that received the cancelled redeem
function cancelRedeemRequest(address controller, address receiver) external;
}
interface IAccountableVault is IERC20, IERC20Metadata, IERC4626, IControllerOperator {
/// @notice Emitted when assets are locked in the vault
/// @param caller The address that locked the assets
/// @param assets The amount of assets locked
event LockAssets(address indexed caller, uint256 assets);
/// @notice Emitted when locked assets are released
/// @param caller The address that released the assets
/// @param assets The amount of assets released
event ReleaseAssets(address indexed caller, uint256 assets);
/// @notice Emitted when shares transferability is changed
/// @param oldStatus The old transferability status
/// @param newStatus The new transferability status
event SharesTransferableSet(bool oldStatus, bool newStatus);
/// @notice Deposits assets and mints shares to receiver
/// @dev Added for ERC7540 compatibility
/// @param assets Amount of assets to deposit
/// @param receiver Address receiving the shares
/// @param controller Controller address for the deposit
/// @return uint256 Amount of shares minted
function deposit(uint256 assets, address receiver, address controller) external returns (uint256);
/// @notice Mints exact amount of shares by depositing assets
/// @dev Added for ERC7540 compatibility
/// @param shares Amount of shares to mint
/// @param receiver Address receiving the shares
/// @param controller Controller address for the mint
/// @return uint256 Amount of assets deposited
function mint(uint256 shares, address receiver, address controller) external returns (uint256);
/// @notice Locks assets in the vault
/// @param assets Amount of assets to lock
function lockAssets(uint256 assets, address sender) external;
/// @notice Releases previously locked assets
/// @param assets Amount of assets to release
function releaseAssets(uint256 assets, address receiver) external;
/// @notice Issues shares to a receiver
/// @param shares Amount of shares to issue
/// @param receiver Address to receive the shares
function mintShares(uint256 shares, address receiver) external;
/// @notice Repossesses shares from a sender
/// @param shares Amount of shares to repossess
/// @param sender Address to repossess the shares from
function burnShares(uint256 shares, address sender) external;
/// @notice Forces a transfer of shares from one address to another
/// @param from The address to transfer shares from
/// @param to The address to transfer shares to
/// @param shares The amount of shares to transfer
function forceTransferShares(address from, address to, uint256 shares) external;
/// @notice Sets the transferability of shares
/// @param transferable True to set shares transferable, false to set them non-transferable
function setSharesTransferable(bool transferable) external;
/// @notice Checks if shares can be transferred
/// @return bool True if shares are transferable
function transferableShares() external view returns (bool);
/// @notice Current asset share ratio
/// @return uint256 Asset share ratio
function assetShareRatio() external view returns (uint256);
/// @notice Gets the current share price
/// @return uint256 Price per share in assets
function sharePrice() external view returns (uint256);
/// @notice The share token address
/// @return address Share token address
function share() external view returns (address);
/// @notice Gets the state of the vault
/// @param controller The controller address for the vault
/// @return state The state of the vault given the controller
function getState(address controller) external view returns (VaultState memory state);
}
interface IAccountableAsyncRedeemVault is IAccountableVault, IAsyncRedeem, IAsyncCancelRedeem, IERC165 {
/// @notice Emitted when a redeem becomes claimable
/// @param controller The controller address for the redemption
/// @param requestId The ID of the redeem request
/// @param assets The amount of assets to be received
/// @param shares The amount of shares redeemed
event RedeemClaimable(address indexed controller, uint256 indexed requestId, uint256 assets, uint256 shares);
}
interface IAccountableAsyncVault is
IAccountableVault,
IAsyncDeposit,
IAsyncRedeem,
IAsyncCancelDeposit,
IAsyncCancelRedeem,
IERC165
{
/// @notice Emitted when a deposit becomes claimable
/// @param controller The controller address for the deposit
/// @param requestId The ID of the deposit request
/// @param assets The amount of assets deposited
/// @param shares The amount of shares to be minted
event DepositClaimable(address indexed controller, uint256 indexed requestId, uint256 assets, uint256 shares);
/// @notice Emitted when a redeem becomes claimable
/// @param controller The controller address for the redemption
/// @param requestId The ID of the redeem request
/// @param assets The amount of assets to be received
/// @param shares The amount of shares redeemed
event RedeemClaimable(address indexed controller, uint256 indexed requestId, uint256 assets, uint256 shares);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
/// @notice Struct tracking a withdrawal request
struct WithdrawalRequest {
/// @notice The number of shares requested
uint256 shares;
/// @notice The controller of the request
address controller;
}
/// @notice Struct tracking the withdrawal queue
struct WithdrawalQueue {
/// @notice The next request ID to be processed
uint128 nextRequestId;
/// @notice The last request ID to be processed
uint128 lastRequestId;
/// @notice The mapping of request IDs to withdrawal requests
mapping(uint128 requestId => WithdrawalRequest request) requests;
}
/// @notice Interface for the withdrawal queue
interface IAccountableWithdrawalQueue {
/// @notice Fulfill a redeem request
/// @param controller The controller of the request
/// @param shares The number of shares to fulfill
function fulfillRedeemRequest(address controller, uint256 shares) external;
/// @notice Process up to `maxShares` shares
/// @param maxShares The maximum number of shares to process
/// @return assetsUsed The number of assets used to process the shares
function processUpToShares(uint256 maxShares) external returns (uint256 assetsUsed);
/// @notice Process up to `maxRequestId` request
/// @param maxRequestId The last request ID to process
/// @return processedShares The number of shares that were processed
/// @return assetsUsed The number of assets used to process the shares
function processUpToRequestId(uint256 maxRequestId)
external
returns (uint256 processedShares, uint256 assetsUsed);
/// @notice Preview required shares that can be processed with `maxAssets`
/// @param maxAssets The maximum number of assets to be matched with `maxAssets`
/// @return processedShares The number of shares that can be processed with the assets
/// @return assetsUsed The number of assets used to process the shares
function previewRequiredShares(uint256 maxAssets)
external
view
returns (uint256 processedShares, uint256 assetsUsed);
/// @notice Preview the last request ID that can be processed with `maxAssets`
/// @param maxAssets The maximum number of assets to be matched with `maxAssets`
/// @return maxRequestId The last request ID that can be processed with the assets
/// @return assetsUsed The number of assets used to process the shares
function previewMaxRequestId(uint256 maxAssets) external view returns (uint256 maxRequestId, uint256 assetsUsed);
/// @notice Get the total number of shares queued
/// @return totalQueuedShares The total number of shares queued
function totalQueuedShares() external view returns (uint256 totalQueuedShares);
/// @notice Get the reserved liquidity
/// @return reservedLiquidity The reserved liquidity
function reservedLiquidity() external view returns (uint256 reservedLiquidity);
/// @notice Get the withdrawal queue indices
/// @return nextRequestId The next request ID to be processed
/// @return lastRequestId The last request ID to be processed
function queue() external view returns (uint128 nextRequestId, uint128 lastRequestId);
/// @notice Get the withdrawal request for a controller
/// @param controller The controller of the request
/// @return request The withdrawal request
function withdrawalRequest(address controller) external view returns (WithdrawalRequest memory request);
/// @notice Get the withdrawal requests
/// @param start The start index of the queue
/// @param end The end index of the queue
/// @return requests The withdrawal requests
function withdrawalRequests(uint128 start, uint128 end)
external
view
returns (WithdrawalRequest[] memory requests);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
import {PermissionLevel} from "./IAccess.sol";
import {IAccountableLoan} from "./IAccountableStrategy.sol";
/// @notice Parameters for initializing the AccountableFixedTerm contract
struct FixedTermInitParams {
/// @notice The asset of the loan
address asset;
/// @notice The global registry address
address globals;
/// @notice The fee manager of the loan
address feeManager;
/// @notice The investment manager of the loan
address investmentManager;
/// @notice The permission level of the loan
PermissionLevel permissionLevel;
/// @notice Whether the shares are transferable
bool sharesTransferable;
/// @notice The name of the share token
string name;
/// @notice The symbol of the share token
string symbol;
}
/// @notice Loan interest parameters
struct InterestParams {
/// @notice Interest rate per interval
uint256 intervalRate;
/// @notice Net potential return of the loan
uint256 netReturn;
/// @notice Number of intervals
uint256 numIntervals;
/// @notice When last payment was made
uint256 lastPaymentTime;
/// @notice Last interval that was paid
uint256 lastPaymentInterval;
}
/// @notice Cache for values needed to calculate interest totals
/// @dev Struct for grouping variables to avoid stack too deep or multiple sloads
struct InterestTotalsCache {
/// @notice The duration of the loan
uint256 duration;
/// @notice The start time of the loan
uint256 startTime;
/// @notice The principal of the loan
uint256 principal;
/// @notice The interest interval of the loan
uint256 interval;
/// @notice The maximum number of intervals of the loan
uint256 maxIntervals;
/// @notice The interval interest rate of the loan
uint256 intervalRate;
/// @notice The last time the interest was paid
uint256 lastPaidTime;
/// @notice The last paid interval of the loan
uint256 lastPaidInterval;
/// @notice The late interest grace period of the loan
uint256 lateGracePeriod;
/// @notice The late interest penalty of the loan
uint256 lateInterestPenalty;
/// @notice Whether the penalties are enabled
bool penaltiesEnabled;
}
/// @notice Interface for the AccountableFixedTerm contract
interface IAccountableFixedTerm is IAccountableLoan {
/// @notice Event emitted when the loan is accepted by borrower
event LoanAccepted();
/// @notice Event emitted when the loan is rejected by borrower
event LoanRejected();
/// @notice Event emitted when the loan is rolled over by borrower
/// @param proposalId The ID of the proposal
/// @param extensionDuration The duration of the extension
/// @param extensionNonce The nonce of the extension
/// @param timeLock The time lock of the proposal
event LoanRolloverRequested(
bytes32 indexed proposalId, uint256 extensionDuration, uint256 extensionNonce, uint256 timeLock
);
/// @notice Event emitted when the loan is rolled over by manager
event LoanRolloverExecuted();
/// @notice Event emitted when accrued interest is claimed by lender
/// @param owner The address of the owner claiming interest
/// @param claimedInterest The amount of interest claimed
event InterestClaimed(address indexed owner, uint256 claimedInterest);
/// @notice Accept the loan in locked mode
function acceptLoanLocked() external;
/// @notice Accept the loan in dynamic mode
function acceptLoanDynamic() external;
/// @notice Reject the loan
function reject() external;
/// @notice Claim accrued interest
function claimInterest() external returns (uint256);
/// @notice Get the interest data
/// @return interest The interest amount
/// @return penalties The penalties amount
/// @return performanceFee The performance fee amount
/// @return establishmentFee The establishment fee amount
/// @return currentInterval The current interval
function interestData()
external
view
returns (
uint256 interest,
uint256 penalties,
uint256 performanceFee,
uint256 establishmentFee,
uint256 currentInterval
);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
/// @notice Struct containing loan terms and state
struct Loan {
/// @notice Minimum deposit amount allowed
uint256 minDeposit;
/// @notice Minimum redeem amount allowed
uint256 minRedeem;
/// @notice Maximum loan amount that can be drawn
/// The full threshold can be met after deposit period if the loan is not locked
uint256 maxCapacity;
/// @notice Minimum loan amount for the loan to be accepted
/// The threshold needs to be met during deposit period or loan can be rejected
uint256 minCapacity;
/// @notice Liquidity ratio that has to be maintained in the vault at all times
/// Expressed in basis points and cannot exceed 1e6, defaults to 0
uint256 reserveThreshold;
/// @notice Principal amount still to be repaid
uint256 outstandingPrincipal;
/// @notice Interest amount still to be paid
uint256 outstandingInterest;
/// @notice all funds accepted and available for borrowing
uint256 drawableFunds;
/// @notice Annual interest rate in basis points
uint256 interestRate;
/// @notice Late interest penalty in basis points
uint256 lateInterestPenalty;
/// @notice Total claimable interest available for users
uint256 claimableInterest;
/// @notice Time interval between interest payments
uint256 interestInterval;
/// @notice Time of loan start
uint256 startTime;
/// @notice Time of terms set
uint256 termsSetTime;
/// @notice Time of terms update
uint256 termsUpdateTime;
/// @notice Loan duration in seconds
uint256 duration;
/// @notice Deposit period in seconds
uint256 depositPeriod;
/// @notice Grace period after deposit period for borrower to accept loan
uint256 acceptGracePeriod;
/// @notice Withdrawal window period in seconds
/// If set it allows redemption requests
uint256 withdrawalPeriod;
/// @notice Late interest payment grace period in seconds
/// This is the time period before a loan is considered delinquent
uint256 lateInterestGracePeriod;
}
/// @notice Struct containing loan terms
struct LoanTerms {
/// @notice Minimum deposit amount allowed
uint256 minDeposit;
/// @notice Minimum redeem amount allowed
uint256 minRedeem;
/// @notice Maximum loan capacity
uint256 maxCapacity;
/// @notice Minimum loan capacity
uint256 minCapacity;
/// @notice Annual interest rate in basis points
uint256 interestRate;
/// @notice Late interest penalty in basis points
uint256 lateInterestPenalty;
/// @notice Late interest grace period in seconds
uint256 lateInterestGracePeriod;
/// @notice Time interval between interest payments
uint256 interestInterval;
/// @notice Duration of the loan in seconds
uint256 duration;
/// @notice Deposit period in seconds
uint256 depositPeriod;
/// @notice Grace period after deposit period for borrower to accept loan
uint256 acceptGracePeriod;
/// @notice Withdrawal window period in seconds
uint256 withdrawalPeriod;
}
/// @notice Struct containing DVN proof data
struct DVNProof {
/// @notice Merkle root of the DVN merkle tree
bytes32 root;
/// @notice Signature of the DVN payload
bytes32 signature;
/// @notice Message hash that was signed
bytes32 messageHash;
}
/// @notice Enum representing different loan states
enum LoanState {
/// @notice Loan terms are not set
Initialized,
/// @notice Loan terms are set
TermsSet,
/// @notice Loan is accepted by borrower and locked
OngoingLocked,
/// @notice Loan is accepted by borrower and dynamic deposits are allowed
OngoingDynamic,
/// @notice Loan is rejected
Rejected,
/// @notice Loan is repaid
Repaid,
/// @notice Loan is in default
InDefault,
/// @notice Loan is in default claims
InDefaultClaims
}
/// @notice Interface for managing strategy vault hooks
interface IStrategyVaultHooks {
/// @notice Hook for handling deposit requests
/// @param share The address of the share token
/// @param assets The amount of assets requested
/// @param controller The address of the controller
/// @param owner The address of the owner of the assets
/// @return canFulfill Whether the deposit can be fulfilled instantly
/// @return price The price of the share
function onRequestDeposit(address share, uint256 assets, address controller, address owner)
external
returns (bool canFulfill, uint256 price);
/// @notice Hook for handling redeem requests
/// @param share The address of the share token
/// @param shares The amount of shares requested
/// @param controller The address of the controller
/// @param owner The address of the owner of the shares
/// @return canFulfill Whether the redeem can be fulfilled instantly
/// @return price The price of the share
function onRequestRedeem(address share, uint256 shares, address controller, address owner)
external
returns (bool canFulfill, uint256 price);
/// @notice Hook for handling cancel deposit requests
/// @param share The address of the share token
/// @param controller The address of the controller
/// @return canCancel Whether the deposit can be cancelled instantly
function onCancelDepositRequest(address share, address controller) external returns (bool canCancel);
/// @notice Hook for handling cancel redeem requests
/// @param share The address of the share token
/// @param controller The address of the controller
/// @return canCancel Whether the redeem can be cancelled instantly
function onCancelRedeemRequest(address share, address controller) external returns (bool canCancel);
/// @notice Hook for handling deposit requests
/// @param share The address of the share token
/// @param assets The amount of assets to deposit
/// @param receiver The address of the receiver
/// @param controller The address of the controller
function onDeposit(address share, uint256 assets, address receiver, address controller)
external
returns (uint256 price);
/// @notice Hook for handling mint requests
/// @param share The address of the share token
/// @param shares The amount of shares to mint
/// @param receiver The address of the receiver
/// @param controller The address of the controller
function onMint(address share, uint256 shares, address receiver, address controller)
external
returns (uint256 price);
/// @notice Hook for handling redeem requests
/// @param share The address of the share token
/// @param shares The amount of shares to redeem
/// @param receiver The address of the receiver
function onRedeem(address share, uint256 shares, address receiver, address controller) external;
/// @notice Hook for handling withdraw requests
/// @param share The address of the share token
/// @param assets The amount of assets to withdraw
/// @param receiver The address of the receiver
/// @param controller The address of the controller
function onWithdraw(address share, uint256 assets, address receiver, address controller) external;
/// @notice Hook for handling vault transfers
/// @param from The address of the sender
/// @param to The address of the receiver
/// @param amount The amount of shares to transfer
function onTransfer(address share, address from, address to, uint256 amount) external;
/// @notice Operator authorization
/// @param operator The address of the operator
/// @dev Reverts for unauthorized operator. Operator should not be an input value of another call
function authOperator(address operator) external;
/// @notice Centralized share price
/// @param share The address of the share token
function sharePrice(address share) external view returns (uint256 price);
/// @notice Centralized accrued assets
/// @param share The address of the share token
/// @return assets The accrued assets to be distributed
function accruedAssets(address share) external view returns (uint256 assets);
/// @notice Centralized max deposit capacity
/// @param share The address of the share token
/// @param controller The address of the controller
/// @return maxAssets The maximum amount of assets that can be deposited
function maxDeposit(address share, address controller) external view returns (uint256 maxAssets);
/// @notice Centralized max redeem capacity
/// @param share The address of the share token
/// @param controller The address of the controller
/// @return maxShares The maximum amount of shares that can be redeemed
function maxRedeem(address share, address controller) external view returns (uint256 maxShares);
}
/// @notice Interface for managing fee manager hooks
interface IFeeManagerHooks {
/// @notice Hook for handling fee structure changes
function onFeeStructureChange() external;
}
interface IUpdateLateStatus {
/// @notice Updates the late status of the loan
function updateLateStatus() external;
}
/// @notice Interface for managing base strategy components
interface IAccountableStrategy is IStrategyVaultHooks, IFeeManagerHooks, IERC165 {
/// @notice Event emitted when the borrower is changed
event BorrowerChanged(address indexed oldBorrower, address indexed newBorrower);
/// @notice Event emitted when the pending borrower is set
event PendingBorrowerSet(address indexed pendingBorrower);
/// @notice Event emitted when the safety module is set
event SafetyModuleSet(address indexed safetyModule);
/// @notice Event emitted when the investment manager is set
event InvestmentManagerSet(address indexed investmentManager);
/// @notice Event emitted when the proof signer is set
event ProofSignerSet(address indexed proofSigner);
/// @notice Event emitted when the DVN proof is published
event DVNProofPublished(bytes32 indexed root, bytes32 indexed signature, bytes32 indexed messageHash);
/// @notice Event emitted when the default is rejected
event LoanDefaultRejected();
/// @notice Event emitted when the default is accepted
event LoanDefaulted(uint256 principal, uint256 collateral);
/// @notice Event emitted when the penalties enabled flag is set
event PenaltiesEnabledSet(bool enabled);
/// @notice Event emitted when the security admin enabled flag is set
event SecurityAdminEnabledSet(bool enabled);
/// @notice Event emitted when the operations admin enabled flag is set
event OperationsAdminEnabledSet(bool enabled);
/// @notice Event emitted when the reserve threshold is set
event ReserveThresholdSet(uint256 threshold);
/// @notice Event emitted when the rewards distributor is set
event RewardsDistributorSet(address indexed rewardsDistributor);
/// @notice Event emitted when the price oracle is set
event PriceOracleSet(address indexed priceOracle);
/// @notice Set a new pending borrower
/// @param newBorrower Address of the new pending borrower
function setPendingBorrower(address newBorrower) external;
/// @notice Accept the borrower role as pending borrower
function acceptBorrowerRole() external;
/// @notice Set a new safety module contract
/// @dev It can act as a factory in a deploy-and-set manner
/// @param safetyModule Address of the new safety module
function setSafetyModule(address safetyModule) external;
/// @notice Set a new rewards distributor contract
/// @dev It can act as a factory in a deploy-and-set manner
/// @param rewards Address of the new rewards distributor
function setRewardsDistributor(address rewards) external;
/// @notice Set a new price oracle contract
/// @param priceOracle Address of the new price oracle
function setPriceOracle(address priceOracle) external;
/// @notice Set a new investment manager contract
/// @param investmentManager Address of the new investment manager
function setInvestmentManager(address investmentManager) external;
/// @notice Set the auth signer address
/// @dev This is function relays data to the vault's access module
/// It sets the kyc verification signer when permission level is `KYC`
/// @param authSigner Address of the new auth signer
function setAuthSigner(address authSigner) external;
/// @notice Set the lenders addresses and permissions
/// @dev This is function relays data to the vault's access module
/// It whitelists lenders when permission level is `Whitelist`
/// @param lenders Addresses of the lenders to be whitelisted
/// @param allowed Whether the lenders are allowed to perform actions
function setLenders(address[] calldata lenders, bool[] calldata allowed) external;
/// @notice Set the proof signer address
/// @param proofSigner Address of the new proof signer
function setProofSigner(address proofSigner) external;
/// @notice Publish a new DVN proof
/// @param proof Latest DVN proof data
function publishDVNProof(DVNProof memory proof) external;
/// @notice Accept the default of the loan
function acceptDefault() external;
/// @notice Reject the default of the loan
function rejectDefault() external;
/// @notice Set the penalties enabled flag
/// @param enabled Whether penalties are enabled
function setPenaltiesEnabled(bool enabled) external;
/// @notice Set the security admin enabled flag
/// @param enabled Whether security admin is enabled
function setSecurityAdminEnabled(bool enabled) external;
/// @notice Set the operations admin enabled flag
/// @param enabled Whether operations admin is enabled
function setOperationsAdminEnabled(bool enabled) external;
/// @notice Set the shares transferable flag
/// @param transferable Whether shares are transferable
function setSharesTransferable(bool transferable) external;
/// @notice Set the min reserve threshold required in the vault
/// @param threshold The threshold expressed in basis points
function setReserveThreshold(uint256 threshold) external;
/// @notice Get the global registry contract address
/// @return The global registry address
function globals() external view returns (address);
/// @notice Get the current borrower address
/// @return The borrower address
function borrower() external view returns (address);
/// @notice Get the pending borrower address
/// @return The pending borrower address
function pendingBorrower() external view returns (address);
/// @notice Get the investment manager contract address
/// @return The investment manager address
function investmentManager() external view returns (address);
/// @notice Get the fee manager contract address
/// @return The fee manager address
function feeManager() external view returns (address);
/// @notice Get the safety module contract address
/// @return The safety module address
function safetyModule() external view returns (address);
/// @notice Get the rewards distributor contract address
/// @return The rewards distributor address
function rewards() external view returns (address);
/// @notice Get the price oracle contract address
/// @return The price oracle address
function priceOracle() external view returns (address);
/// @notice Get the proof signer address
/// @return The proof signer address
/// @dev Returns the pub key of the node signing proofs
function proofSigner() external view returns (address);
/// @notice Get the vault contract address
/// @return The vault address
function vault() external view returns (address);
/// @notice Get the current loan state
/// @return The loan struct
function loan() external view returns (Loan memory);
/// @notice Get the DVN proof
/// @return The latest DVN proof data
function dvnProof() external view returns (DVNProof memory);
/// @notice Get the loan state
/// @return The loan state enum value
function loanState() external view returns (LoanState);
/// @notice Get the penalties enabled flag
/// @return The penalties enabled flag
function penaltiesEnabled() external view returns (bool);
/// @notice Get the security admin enabled flag
/// @return The security admin enabled flag
function securityAdminEnabled() external view returns (bool);
/// @notice Get the operations admin enabled flag
/// @return The operations admin enabled flag
function operationsAdminEnabled() external view returns (bool);
/// @notice Get the precision for the strategy
/// @return The precision
function PRECISION() external view returns (uint256);
/// @notice Get the basis points for the strategy
/// @return The basis points
function BASIS_POINTS() external view returns (uint256);
/// @notice Get the version of the strategy
/// @dev Increment when upgrading the strategy
function version() external view returns (uint256);
}
/// @notice Interface for managing loan lifecycle and payments
interface IAccountableLoan is IAccountableStrategy {
/// @notice Event emitted when the loan is initialized
/// @param minCapacity The minimum loan capacity
/// @param maxCapacity The maximum loan capacity
/// @param interestRate The interest rate of the loan
/// @param lateInterestPenalty The late interest penalty
/// @param interestInterval The interest interval
/// @param duration The duration of the loan
event LoanTermsSet(
uint256 minCapacity,
uint256 maxCapacity,
uint256 interestRate,
uint256 lateInterestPenalty,
uint256 interestInterval,
uint256 duration
);
/// @notice Event emitted when the default is initiated
event LoanDefaultInitiated();
/// @notice Event emitted when a default is covered by safety collateral
/// @param safetyModule The address of the safety module
/// @param provider The address of the provider
/// @param collateral The amount of collateral covered
event DefaultCovered(address indexed safetyModule, address indexed provider, uint256 collateral);
/// @notice Event emitted when funds are borrowed
/// @param borrower The address of the borrower
/// @param assets The amount of funds borrowed
event Borrowed(address indexed borrower, uint256 assets);
/// @notice Event emitted when the loan is repaid
/// @param assets The amount of funds repaid
event LoanRepaid(uint256 assets);
/// @notice Event emitted when the loan is partially repaid
/// @param amount The amount of funds repaid to close loan
/// @param remainingInterest The remaining interest amount
event LoanPrepaid(uint256 amount, uint256 remainingInterest);
/// @notice Event emitted when interest is paid
/// @param borrower The address of the borrower
/// @param totalAmount The total amount paid
/// @param interestPenalty The interest penalty amount paid
/// @param performanceFee The performance fee amount paid
/// @param establishmentFee The establishment fee amount paid
/// @param paymentInterval The interval of the payment
event InterestPaid(
address indexed borrower,
uint256 totalAmount,
uint256 interestPenalty,
uint256 performanceFee,
uint256 establishmentFee,
uint256 paymentInterval
);
/// @notice Initialize a new loan with the given terms
/// @param terms The loan terms to initialize with
function setTerms(LoanTerms memory terms) external;
/// @notice Update an existing loan with new terms
/// @param terms The new loan terms to update to
function updateTerms(LoanTerms memory terms) external;
/// @notice Initialize loan default state
function defaultLoan() external;
/// @notice Cover the default of the loan
/// @param assets The amount of assets to cover
function coverDefault(uint256 assets) external;
/// @notice Borrow funds from the loan
/// @param assets The amount of funds to borrow
function borrow(uint256 assets) external;
/// @notice Repay the loan amount
/// @param assets The amount of funds to repay
function repay(uint256 assets) external;
/// @notice Make a partial loan repayment
function prepay() external;
/// @notice Make an interest payment
/// @param assets The interest amount to pay
function pay(uint256 assets) external;
/// @notice Get the period of time the loan has been delinquent
/// @return The period of time the loan has been delinquent
function timeDelinquent() external view returns (uint256);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
import "../constants/Errors.sol";
import {RewardsType} from "../interfaces/IRewards.sol";
import {IAccess, PermissionLevel} from "../interfaces/IAccess.sol";
import {IRewardsFactory} from "../interfaces/IRewardsFactory.sol";
import {IAsyncVaultFactory} from "../interfaces/IAsyncVaultFactory.sol";
import {IGlobalRegistry} from "../interfaces/IGlobalRegistry.sol";
import {IAccountableVault} from "../interfaces/IAccountableAsyncVault.sol";
import {
IAccountableStrategy,
IStrategyVaultHooks,
IFeeManagerHooks,
LoanState,
LoanTerms,
DVNProof,
Loan
} from "../interfaces/IAccountableStrategy.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import {StrategyStorage} from "./storage/StrategyStorage.sol";
/// @title AccountableStrategy
/// @notice Abstract contract that implements the IAccountableStrategy interface
/// @dev It holds the state of an accountable strategy and implements the IAccountableStrategy interface
/// @custom:security-contact [email protected]
abstract contract AccountableStrategy is PausableUpgradeable, StrategyStorage {
// ========================================================================== //
// Constants //
// ========================================================================== //
/// @dev precision for calculations
uint256 public constant PRECISION = 1e36;
/// @dev 1e6 basis points representing 100%
uint256 public constant BASIS_POINTS = 1e6;
/// @dev seconds in a day
uint256 public constant DAYS_1_SECONDS = 1 days;
/// @dev seconds in a year considered 365 days
uint256 public constant DAYS_365_SECONDS = 365 days;
// ========================================================================== //
// Modifiers //
// ========================================================================== //
/// @notice modifier to check if caller is the borrower
modifier onlyBorrower() {
if (msg.sender != borrower) revert Unauthorized();
_;
}
/// @notice modifier to check if caller is the investment manager
modifier onlyManager() {
if (msg.sender != investmentManager) revert Unauthorized();
_;
}
/// @notice modifier to check if caller is the safety module or the manager
modifier onlySafetyModuleOrManager() {
if (safetyModule != address(0)) {
revert_AtLeastOne(safetyModule, investmentManager);
} else {
if (msg.sender != investmentManager) revert Unauthorized();
}
_;
}
/// @notice modifier to check if caller is the manager or the security admin (if enabled)
modifier onlyManagerOrSecurityAdmin() {
if (securityAdminEnabled) {
revert_AtLeastOne(IGlobalRegistry(globals).securityAdmin(), investmentManager);
} else {
if (msg.sender != investmentManager) revert Unauthorized();
}
_;
}
/// @notice modifier to check if caller is the manager or the operations admin (if enabled)
modifier onlyManagerOrOperationsAdmin() {
if (operationsAdminEnabled) {
revert_AtLeastOne(IGlobalRegistry(globals).operationsAdmin(), investmentManager);
} else {
if (msg.sender != investmentManager) revert Unauthorized();
}
_;
}
/// @notice modifier to check if caller is the proof signer
modifier onlyProofSigner() {
if (msg.sender != proofSigner) revert Unauthorized();
_;
}
/// @notice modifier to check if caller is the vault
modifier onlyVault() {
if (msg.sender != vault) revert Unauthorized();
_;
}
/// @dev Reverts if the caller is not one of the two addresses
function revert_AtLeastOne(address a, address b) internal view {
if (msg.sender != a && msg.sender != b) revert Unauthorized();
}
// ========================================================================== //
// Pausable Functions //
// ========================================================================== //
/// @dev Pauses the contract
function pause() public onlyManagerOrSecurityAdmin {
if (msg.sender == IGlobalRegistry(globals).securityAdmin()) {
_pausedByAdmin = true;
}
_pause();
}
/// @dev Unpauses the contract and checks whether msg.sender is admin when loan was paused by admin
function unpause() public onlyManagerOrSecurityAdmin {
if (_pausedByAdmin && msg.sender != IGlobalRegistry(globals).securityAdmin()) {
revert Unauthorized();
}
_pausedByAdmin = false;
_unpause();
}
// ========================================================================== //
// Configuration Functions //
// ========================================================================== //
/// @inheritdoc IAccountableStrategy
function setPendingBorrower(address newBorrower) external virtual onlyManager {
if (newBorrower == address(0)) revert ZeroAddress();
pendingBorrower = newBorrower;
emit PendingBorrowerSet(newBorrower);
}
/// @inheritdoc IAccountableStrategy
function acceptBorrowerRole() external virtual {
if (msg.sender != pendingBorrower) revert InvalidPendingBorrower();
address oldBorrower = borrower;
borrower = msg.sender;
pendingBorrower = address(0);
emit BorrowerChanged(oldBorrower, msg.sender);
}
/// @inheritdoc IAccountableStrategy
function setInvestmentManager(address investmentManager_) external virtual onlyManager {
if (investmentManager_ == address(0)) revert ZeroAddress();
investmentManager = investmentManager_;
emit InvestmentManagerSet(investmentManager_);
}
/// @inheritdoc IAccountableStrategy
function setAuthSigner(address authSigner_) external onlyManager {
IAccess(vault).setSigner(authSigner_);
}
/// @inheritdoc IAccountableStrategy
function setLenders(address[] calldata lenders, bool[] calldata allowed) external onlyManager {
IAccess(vault).setAllowed(lenders, allowed);
}
/// @inheritdoc IAccountableStrategy
function setProofSigner(address proofSigner_) external virtual onlyManager {
if (proofSigner_ == address(0)) revert ZeroAddress();
proofSigner = proofSigner_;
emit ProofSignerSet(proofSigner_);
}
/// @inheritdoc IAccountableStrategy
function publishDVNProof(DVNProof memory proof) external virtual onlyProofSigner {
_dvnProof = proof;
emit DVNProofPublished(proof.root, proof.signature, proof.messageHash);
}
/// @inheritdoc IAccountableStrategy
function acceptDefault() external {
if (block.timestamp < _defaultValidAt || _defaultValidAt == 0) revert TimelockNotExpired();
loanState = LoanState.InDefault;
emit LoanDefaulted(_loan.outstandingPrincipal, IAccountableVault(vault).totalAssets());
}
/// @inheritdoc IAccountableStrategy
function rejectDefault() external onlyManager {
_defaultValidAt = 0;
emit LoanDefaultRejected();
}
/// @inheritdoc IAccountableStrategy
function setPenaltiesEnabled(bool enabled) external virtual onlyManager {
penaltiesEnabled = enabled;
emit PenaltiesEnabledSet(enabled);
}
/// @inheritdoc IAccountableStrategy
function setSecurityAdminEnabled(bool enabled) external virtual onlyManager {
securityAdminEnabled = enabled;
emit SecurityAdminEnabledSet(enabled);
}
/// @inheritdoc IAccountableStrategy
function setOperationsAdminEnabled(bool enabled) external virtual onlyManager {
operationsAdminEnabled = enabled;
emit OperationsAdminEnabledSet(enabled);
}
/// @inheritdoc IAccountableStrategy
function setSharesTransferable(bool transferable) external virtual onlyManager {
IAccountableVault(vault).setSharesTransferable(transferable);
}
/// @inheritdoc IAccountableStrategy
function setReserveThreshold(uint256 threshold) external virtual onlyManager {
if (threshold > BASIS_POINTS) revert ThresholdTooHigh();
_loan.reserveThreshold = threshold;
emit ReserveThresholdSet(threshold);
}
/// @inheritdoc IAccountableStrategy
function setSafetyModule(address safetyModule_) external virtual onlyManager {
if (safetyModule_ == address(0)) revert ZeroAddress();
safetyModule = safetyModule_;
emit SafetyModuleSet(safetyModule_);
}
/// @inheritdoc IAccountableStrategy
function setRewardsDistributor(address rewards_) external virtual onlyManager {
if (rewards_ == address(0)) revert ZeroAddress();
rewards = rewards_;
emit RewardsDistributorSet(rewards_);
}
/// @inheritdoc IAccountableStrategy
function setPriceOracle(address priceOracle_) external virtual onlyManager {
_requireLoanNotOngoing();
if (priceOracle_ == address(0)) revert ZeroAddress();
priceOracle = priceOracle_;
emit PriceOracleSet(priceOracle_);
}
// ========================================================================== //
// View Functions //
// ========================================================================== //
/// @inheritdoc IAccountableStrategy
function loan() external view returns (Loan memory) {
return _loan;
}
/// @inheritdoc IAccountableStrategy
function dvnProof() external view returns (DVNProof memory) {
return _dvnProof;
}
// ========================================================================== //
// Strategy Vault Hooks //
// ========================================================================== //
/// @inheritdoc IStrategyVaultHooks
function onRequestDeposit(address share, uint256, address, address)
public
virtual
onlyVault
whenNotPaused
returns (bool canFulfill, uint256 price)
{
return (false, _sharePrice(share));
}
/// @inheritdoc IStrategyVaultHooks
function onRequestRedeem(address share, uint256, address, address)
public
virtual
onlyVault
whenNotPaused
returns (bool canFulfill, uint256 price)
{
return (false, _sharePrice(share));
}
/// @inheritdoc IStrategyVaultHooks
function onCancelDepositRequest(address, address) public virtual onlyVault whenNotPaused returns (bool canCancel) {
return true;
}
/// @inheritdoc IStrategyVaultHooks
function onCancelRedeemRequest(address, address) public virtual onlyVault whenNotPaused returns (bool canCancel) {
return true;
}
/// @inheritdoc IStrategyVaultHooks
function onDeposit(address share, uint256, address, address)
public
virtual
onlyVault
whenNotPaused
returns (uint256 price)
{
price = _sharePrice(share);
}
/// @inheritdoc IStrategyVaultHooks
function onMint(address share, uint256, address, address)
public
virtual
onlyVault
whenNotPaused
returns (uint256 price)
{
price = _sharePrice(share);
}
/// @inheritdoc IStrategyVaultHooks
function onRedeem(address, uint256, address, address) public virtual onlyVault whenNotPaused {}
/// @inheritdoc IStrategyVaultHooks
function onWithdraw(address, uint256, address, address) public virtual onlyVault whenNotPaused {}
/// @inheritdoc IStrategyVaultHooks
function onTransfer(address, address, address, uint256) public virtual onlyVault whenNotPaused {}
/// @inheritdoc IStrategyVaultHooks
function authOperator(address operator) external virtual {
if (msg.sender != vault) revert Unauthorized();
if (operator != investmentManager && operator != borrower) revert Unauthorized();
}
/// @inheritdoc IStrategyVaultHooks
function sharePrice(address share) external view virtual returns (uint256 price) {
return _sharePrice(share);
}
/// @dev Computes the share price. To be overridden by the derived strategy
function _sharePrice(address share) internal view virtual returns (uint256 price);
/// @inheritdoc IStrategyVaultHooks
function accruedAssets(address share) external view virtual returns (uint256 assets) {
assets = _accruedAssets(share);
}
/// @dev Computes the accrued assets. To be overridden by the derived strategy
function _accruedAssets(address share) internal view virtual returns (uint256 assets);
/// @inheritdoc IStrategyVaultHooks
function maxDeposit(address, address) public view virtual returns (uint256 maxAssets) {
return type(uint256).max;
}
/// @inheritdoc IStrategyVaultHooks
function maxRedeem(address, address) public view virtual returns (uint256 maxShares) {
return type(uint256).max;
}
// ========================================================================== //
// Fee Manager Hooks //
// ========================================================================== //
/// @inheritdoc IFeeManagerHooks
function onFeeStructureChange() public virtual whenNotPaused {
if (msg.sender != feeManager) revert Unauthorized();
}
/// @inheritdoc IAccountableStrategy
function version() external view virtual returns (uint256) {
return 1;
}
// ========================================================================== //
// Internal Helper Functions //
// ========================================================================== //
/// @dev Calls the factory to create a new async redeem vault
function _makeVault(
IERC20 asset_,
bool sharesTransferable_,
PermissionLevel permissionLevel_,
string memory name_,
string memory symbol_
) internal {
vault = IAsyncVaultFactory(IGlobalRegistry(globals).vaultFactory()).createAsyncRedeemVault(
asset_, address(this), sharesTransferable_, permissionLevel_, name_, symbol_, PRECISION
);
}
/// @dev Sets the loan terms and initializes the loan state
function _setTerms(LoanTerms memory terms) internal {
_loan = Loan({
lateInterestGracePeriod: terms.lateInterestGracePeriod == 0 ? 2 days : terms.lateInterestGracePeriod,
acceptGracePeriod: terms.acceptGracePeriod == 0 ? 2 days : terms.acceptGracePeriod,
depositPeriod: terms.depositPeriod,
withdrawalPeriod: terms.withdrawalPeriod,
lateInterestPenalty: terms.lateInterestPenalty,
interestInterval: terms.interestInterval,
interestRate: terms.interestRate,
minDeposit: terms.minDeposit,
minRedeem: terms.minRedeem,
maxCapacity: terms.maxCapacity,
minCapacity: terms.minCapacity,
duration: terms.duration,
reserveThreshold: 0,
outstandingPrincipal: 0,
outstandingInterest: 0,
claimableInterest: 0,
drawableFunds: 0,
termsSetTime: 0,
termsUpdateTime: 0,
startTime: 0
});
}
/// @dev Checks that the loan terms have been set
function _requireLoanTermsSet() internal view {
if (loanState != LoanState.TermsSet) revert LoanTermsNotSet();
}
/// @dev Checks that the loan terms have not been set
function _requireLoanTermsNotSet() internal view {
if (loanState != LoanState.Initialized) revert LoanTermsAlreadySet();
}
/// @dev Checks that the loan is not ongoing
function _requireLoanNotOngoing() internal view {
if (loanState == LoanState.OngoingLocked || loanState == LoanState.OngoingDynamic) revert LoanOngoing();
}
/// @dev Checks that the loan is ongoing
function _requireLoanOngoing() internal view {
if (loanState != LoanState.OngoingLocked && loanState != LoanState.OngoingDynamic) revert LoanNotOngoing();
}
/// @dev Checks that the loan is in default
function _requireLoanInDefault() internal view {
if (loanState != LoanState.InDefault) revert LoanNotInDefault();
}
/// @dev Checks that the deposit amount is greater than the minimum deposit amount
function _requireMinDepositAmount(uint256 amount) internal view {
if (amount < _loan.minDeposit) revert InsufficientAmount();
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
import {IAccountableFixedTerm, InterestParams} from "../../interfaces/IAccountableFixedTerm.sol";
/// @title FixedTermStorage
/// @notice Storage layout for AccountableFixedTerm with upgrade safety gap
/// @dev Centralizes storage to avoid collisions and simplify upgradeable contract development
/// All storage variables MUST be declared here
abstract contract FixedTermStorage is IAccountableFixedTerm {
/// @dev Loan interest parameters
InterestParams internal _interestParams;
/// @dev Accumulator for interest per share
uint256 internal _accInterestPerShare;
/// @dev Accumulator for interest index per user
mapping(address user => uint256 index) internal _userIndex;
/// @dev Accumulator for pending interest per user
mapping(address user => uint256 interest) internal _pendingInterest;
/// @notice address of base token
address public asset;
/// @dev Gap for future storage variables in AccountableFixedTerm
/// @dev Reserves 50 slots for future additions to AccountableFixedTerm
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
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: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.20;
import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable __self = address(this);
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
* and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev The call is from an unauthorized context.
*/
error UUPSUnauthorizedCallContext();
/**
* @dev The storage `slot` is unsupported as a UUID.
*/
error UUPSUnsupportedProxiableUUID(bytes32 slot);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
_checkProxy();
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
_checkNotDelegated();
_;
}
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/**
* @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data);
}
/**
* @dev Reverts if the execution is not performed via delegatecall or the execution
* context is not of a proxy with an ERC1967-compliant implementation pointing to self.
* See {_onlyProxy}.
*/
function _checkProxy() internal view virtual {
if (
address(this) == __self || // Must be called through delegatecall
ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
) {
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Reverts if the execution is performed via delegatecall.
* See {notDelegated}.
*/
function _checkNotDelegated() internal view virtual {
if (address(this) != __self) {
// Must not be called through delegatecall
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
*
* As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
* is expected to be the implementation slot in ERC1967.
*
* Emits an {IERC1967-Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
revert UUPSUnsupportedProxiableUUID(slot);
}
ERC1967Utils.upgradeToAndCall(newImplementation, data);
} catch {
// The implementation is not UUPS
revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
/// @notice Interface for recoverable tokens
interface IRecoverable {
/// @notice Recovers tokens from the contract
/// @param token The address of the token to recover
/// @param to The address to send the recovered tokens to
/// @param amount The amount of tokens to recover
function recoverTokens(address token, address to, uint256 amount) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";
/**
* @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
* https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
*/
interface IERC4626 is IERC20, IERC20Metadata {
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed sender,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
/**
* @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
*
* - MUST be an ERC-20 token contract.
* - MUST NOT revert.
*/
function asset() external view returns (address assetTokenAddress);
/**
* @dev Returns the total amount of the underlying asset that is “managed” by Vault.
*
* - SHOULD include any compounding that occurs from yield.
* - MUST be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT revert.
*/
function totalAssets() external view returns (uint256 totalManagedAssets);
/**
* @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToShares(uint256 assets) external view returns (uint256 shares);
/**
* @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
* through a deposit call.
*
* - MUST return a limited value if receiver is subject to some deposit limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
* - MUST NOT revert.
*/
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
* call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
* in the same transaction.
* - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
* deposit would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewDeposit(uint256 assets) external view returns (uint256 shares);
/**
* @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* deposit execution, and are accounted for during deposit.
* - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
* - MUST return a limited value if receiver is subject to some mint limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
* - MUST NOT revert.
*/
function maxMint(address receiver) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
* in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
* same transaction.
* - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
* would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by minting.
*/
function previewMint(uint256 shares) external view returns (uint256 assets);
/**
* @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
* execution, and are accounted for during mint.
* - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function mint(uint256 shares, address receiver) external returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
* Vault, through a withdraw call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
* call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
* called
* in the same transaction.
* - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
* the withdrawal would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
/**
* @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* withdraw execution, and are accounted for during withdraw.
* - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
* through a redeem call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxRedeem(address owner) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
* in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
* same transaction.
* - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
* redemption would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by redeeming.
*/
function previewRedeem(uint256 shares) external view returns (uint256 assets);
/**
* @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* redeem execution, and are accounted for during redeem.
* - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @notice Struct containing transaction authentication data (EIP-712 typed)
/// @dev Chain ID is part of the EIP-712 domain separator, not this struct
struct TxAuthData {
/// @notice The calldata of the function being called (selector + encoded args)
bytes functionCallData;
/// @notice The address of the contract where the transaction is being executed
address contractAddress;
/// @notice The address of the account executing/controlling the transaction
address account;
/// @notice The per-account nonce (prevents replay)
uint256 nonce;
/// @notice The block number after which the transaction is considered expired
uint256 blockExpiration;
}
/// @notice Struct containing verification data for verifyMany function
struct VerifyManyData {
/// @notice The function call data extracted from msgData
bytes argsWithSelector;
/// @notice The block expiration timestamp
uint256 blockExpiration;
/// @notice The start position of signatures in msgData
uint256 signaturesStart;
/// @notice The number of signatures to verify
uint256 numSignatures;
}
/// @notice Enum representing different permission levels
enum PermissionLevel {
/// @notice No permission
None,
/// @notice KYC based permission
KYC,
/// @notice Whitelist based permission
Whitelist
}
/// @title Whitelistable Interface
/// @notice Interface for managing whitelisted accounts
interface IWhitelistable {
/// @notice Emitted when allowed status is set for an account
/// @param account The address of the account
/// @param allowed The allowed status
event AllowedSet(address indexed account, bool allowed);
/// @notice Set allowed accounts
/// @param accounts Array of account addresses
/// @param allowed Array of allowed status corresponding to acounts
function setAllowed(address[] calldata accounts, bool[] calldata allowed) external;
/// @notice Check if an account is allowed
/// @param account The address to check allowed status for
/// @return bool True if the account is allowed, false otherwise
function allowed(address account) external view returns (bool);
/// @notice Check if multiple accounts are allowed
/// @param accounts Array of account addresses
/// @return bool True if the accounts are allowed, false otherwise
function allowedMany(address[] calldata accounts) external view returns (bool);
}
/// @title Authorizable Interface
/// @notice Interface for managing authorizable accounts
interface IAuthorizable {
/// @notice Emitted when the signer address is changed
/// @param oldSigner The old signer address
/// @param newSigner The new signer address
event SignerChanged(address indexed oldSigner, address indexed newSigner);
/// @notice Emitted when a transaction authentication data is verified
/// @param chainID The chain ID where the transaction is intended to be processed
/// @param nonce The nonce of the user being verified to prevent replay attacks
/// @param blockExpiration The block number after which the transaction is considered expired
/// @param contractAddress The address of the contract where the transaction is being executed
/// @param userAddress The address of the user executing the transaction
/// @param functionCallData The calldata of the function being called
event TxAuthDataVerified(
uint256 chainID,
uint256 nonce,
uint256 blockExpiration,
address indexed contractAddress,
address indexed userAddress,
bytes functionCallData
);
/// @notice Set the signer address
/// @param signer The address of the signer
function setSigner(address signer) external;
/// @notice Get the signer address
/// @return The signer address
function signer() external view returns (address);
/// @notice Get the nonce for a given user
/// @param user The address of the user
/// @return The nonce of the user
function nonces(address user) external view returns (uint256);
/// @notice Get the message hash for a given transaction authentication data
/// @param txAuthData The transaction authentication data
/// @return The message hash
function getMessageHash(TxAuthData calldata txAuthData) external view returns (bytes32);
}
/// @title Access Interface
/// @notice Interface for managing access to a contract
interface IAccess is IAuthorizable, IWhitelistable {
/// @notice Get the permission level for the loan
/// @return The permission level enum value
function permissionLevel() external view returns (PermissionLevel);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
import {IRecoverable} from "./IRecoverable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
/// @notice Pending root for rewards distribution
struct PendingRoot {
/// @notice The root of the merkle tree
bytes32 root;
/// @notice The timestamp when the root is valid at
uint256 validAt;
}
/// @notice Reward struct
struct Reward {
/// @notice The total amount of reward assets without accrued rewards
uint256 totalWithoutRewards;
/// @notice The total amount of reward assets borrowed
/// @dev This amount is an always increasing counter
uint256 totalBorrowed;
/// @notice The total amount of reward assets repaid
/// @dev This amount is an always increasing counter
/// @dev The delta between repaid and borrowed can be used to calculate performance
/// @dev Greater repaid amount means a positive performance that returned profits to the strategy
/// @dev In cases of negative delta, the offset from accrued rewards can compensate for the negative returns
uint256 totalRepaid;
}
/// @notice The type of rewards distribution
/// @dev Used for integrators to determine the type of rewards distribution
enum RewardsType {
/// @notice No rewards distribution
None,
/// @notice Rewards distribution via merkle proofs
Merkle,
/// @notice Rewards distribution via strategy
Strategy
}
/// @notice Base interface for rewards modules
interface IRewardsBase is IRecoverable, IERC165 {
/// @notice Emitted when rewards are locked
/// @param amount The amount of rewards locked
/// @param asset The address of the asset being locked
/// @param owner The address of the asset owner
event RewardLocked(uint256 amount, address indexed asset, address indexed owner);
/// @notice Emitted when rewards are borrowed
/// @param amount The amount of rewards borrowed
/// @param asset The address of the asset being borrowed
/// @param receiver The address of the receiver
event RewardBorrowed(uint256 amount, address indexed asset, address indexed receiver);
/// @notice Emitted when rewards are repaid
/// @param amount The amount of rewards repaid
/// @param asset The address of the asset being repaid
/// @param owner The address of the owner of the rewards
event RewardRepaid(uint256 amount, address indexed asset, address indexed owner);
/// @notice Emitted when rewards are added
/// @param amount The amount of rewards added
/// @param asset The address of the asset being added
/// @param owner The address of the asset owner
event RewardAdded(uint256 amount, address indexed asset, address indexed owner);
/// @notice Emitted when an updater is set
/// @param updater The address of the updater
/// @param allowed Whether the updater is allowed
event UpdaterSet(address indexed updater, bool allowed);
/// @notice Locks underlying rewards in the rewards module
/// @param amount The amount of rewards to lock
/// @param asset The address of the asset being locked
/// @param owner The address of the asset owner
/// @dev This MUST be a strategy downstream authorized call
/// @dev This MUST revert for rewards distributions that don't require locked underlying asset
function lock(uint256 amount, address asset, address owner) external;
/// @notice Borrows underlying rewards from the rewards module
/// @param amount The amount of rewards to borrow
/// @param asset The address of the asset being borrowed
/// @param receiver The address of the receiver
function borrow(uint256 amount, address asset, address receiver) external;
/// @notice Repays underlying rewards to the rewards module
/// @param amount The amount of rewards to repay
/// @param asset The address of the asset being repaid
/// @param owner The address of the owner of the rewards
function repay(uint256 amount, address asset, address owner) external;
/// @notice Adds rewards to the rewards module
/// @param amounts The amounts of rewards to add
/// @param assets The addresses of the assets being added
/// @param owner The address of the owner of the rewards
function addRewards(uint256[] calldata amounts, address[] calldata assets, address owner) external;
/// @notice Adds a reward to the rewards module
/// @param amount The amount of rewards to add
/// @param asset The address of the asset being added
/// @param owner The address of the owner of the rewards
/// @dev This assumes the root will be set independently
function addReward(uint256 amount, address asset, address owner) external;
/// @notice Returns the total value locked in the rewards module
/// @return tvl The total value locked in the rewards module
function rewardState(address asset) external view returns (Reward memory);
/// @notice Sets an updater
/// @param updater The address of the updater
/// @param allowed Whether the updater is allowed
function setUpdater(address updater, bool allowed) external;
/// @notice Returns the strategy address
/// @return strategy The strategy address
function strategy() external view returns (address);
/// @notice Returns whether an updater is allowed
/// @param updater The address of the updater
/// @return allowed Whether the updater is allowed
function updaters(address updater) external view returns (bool);
/// @notice Returns the total rewards for an asset
/// @param asset The address of the asset
/// @return totalRewards The total rewards for an asset
function totalRewards(address asset) external view returns (uint256);
}
/// @notice Base interface for rewards distributor
interface IRewardsDistributorBase {
/// @notice Emitted when a user claims rewards
/// @param account The address of the user who claims rewards
/// @param asset The address of the asset being claimed
/// @param amount The amount of rewards claimed
event Claimed(address indexed account, address indexed asset, uint256 amount);
/// @notice Claims available rewards
/// @param account The address of the user who claims rewards
/// @param asset The address of the asset being claimed
/// @param claimable The amount of rewards claimable
/// @param proof The proof of the merkle tree
/// @return amount The amount of rewards claimed
/// @dev Can be an authorized call depending on distribution model
function claim(address account, address asset, uint256 claimable, bytes32[] calldata proof)
external
returns (uint256 amount);
/// @notice Returns the amount of rewards claimed for an account and asset
/// @param account The address of the user who claims rewards
/// @param asset The address of the asset being claimed
/// @return amount The amount of rewards claimed
function claimed(address account, address asset) external view returns (uint256 amount);
}
/// @notice Interface for rewards distributor with merkle proofs
interface IRewardsDistributorMerkle is IRewardsDistributorBase {
/// @notice Emitted when the root is set
/// @param oldRoot The old root
/// @param newRoot The new root
event RootSet(bytes32 indexed oldRoot, bytes32 indexed newRoot);
/// @notice Emitted when the timelock is set
/// @param timelock The timelock
event TimelockSet(uint256 timelock);
/// @notice Emitted when a pending root is set
/// @param updater The address of the updater who set the pending root
/// @param root The root of the pending root
event PendingRootSet(address indexed updater, bytes32 indexed root);
/// @notice Emitted when a pending root is revoked
/// @param updater The address of the updater who revoked the pending root
event PendingRootRevoked(address indexed updater);
/// @notice Sets the root
/// @param root The root
function setRoot(bytes32 root) external;
/// @notice Submits a pending root
/// @param root The root
function submitRoot(bytes32 root) external;
/// @notice Accepts a pending root
function acceptRoot() external;
/// @notice Revokes a pending root
function revokePendingRoot() external;
/// @notice Sets the timelock
/// @param timelock The timelock
function setTimelock(uint256 timelock) external;
/// @notice Returns the root
/// @return root The root
function root() external view returns (bytes32);
/// @notice Returns the pending root
/// @return pendingRoot The pending root
function pendingRoot() external view returns (PendingRoot memory);
/// @notice Returns the timelock
/// @return timelock The timelock
function timelock() external view returns (uint256);
}
interface IRewardsDistributorStrategy is IRewardsDistributorBase {}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
import {RewardsType} from "../interfaces/IRewards.sol";
/// @title IRewardsFactory
/// @notice Interface for creating rewards distributors
interface IRewardsFactory {
/// @notice Emitted when a new rewards distributor is created
/// @param rewards The address of the created rewards distributor
event RewardsDistributorCreated(address indexed rewards);
/// @notice Creates a new rewards distributor
/// @param owner The owner of the rewards distributor
/// @param strategy The strategy that will use the rewards distributor
/// @param timelock The timelock for root acceptence in merkle distributor
/// @param rewardsType The type of rewards distributor to create
/// @return The address of the created rewards distributor
function createRewardsDistributor(address owner, address strategy, uint256 timelock, RewardsType rewardsType)
external
returns (address);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
import {PermissionLevel} from "./IAccess.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// @title IAsyncVaultFactory
/// @notice Interface for creating async vaults
interface IAsyncVaultFactory {
/// @notice Emitted when a new async redeem vault is created
/// @param vault The address of the created vault
event AsyncRedeemVaultCreated(address indexed vault);
/// @notice Creates a new async redeem vault
/// @param asset The asset token address for the vault
/// @param proxy The proxy address for the vault
/// @param sharesTransferable Whether shares are transferable
/// @param permissionLevel The permission level for the vault
/// @param name The name of the vault
/// @param symbol The symbol of the vault
/// @param precision The precision for the vault
function createAsyncRedeemVault(
IERC20 asset,
address proxy,
bool sharesTransferable,
PermissionLevel permissionLevel,
string memory name,
string memory symbol,
uint256 precision
) external returns (address);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
interface IGlobalRegistry {
/// @notice Emitted when the security admin is set
/// @param securityAdmin The address of the security admin
event SecurityAdminSet(address securityAdmin);
/// @notice Emitted when the operations admin is set
/// @param operationsAdmin The address of the operations admin
event OperationsAdminSet(address operationsAdmin);
/// @notice Emitted when the treasury is set
/// @param treasury The address of the treasury
event TreasurySet(address treasury);
/// @notice Emitted when the vault factory is set
/// @param vaultFactory The address of the vault factory
event VaultFactorySet(address vaultFactory);
/// @notice Emitted when the rewards factory is set
/// @param rewardsFactory The address of the rewards factory
event RewardsFactorySet(address rewardsFactory);
/// @notice Sets the security admin
/// @param securityAdmin The address of the security admin
function setSecurityAdmin(address securityAdmin) external;
/// @notice Sets the operations admin
/// @param operationsAdmin The address of the operations admin
function setOperationsAdmin(address operationsAdmin) external;
/// @notice Sets the treasury
/// @param treasury The address of the treasury
function setTreasury(address treasury) external;
/// @notice Sets the vault factory
/// @param vaultFactory The address of the vault factory
function setVaultFactory(address vaultFactory) external;
/// @notice Sets the rewards factory
/// @param rewardsFactory The address of the rewards factory
function setRewardsFactory(address rewardsFactory) external;
/// @notice Protocol security admin
/// @return The address of the Accountablesecurity admin
function securityAdmin() external view returns (address);
/// @notice Protocol operations admin
/// @return The address of the Accountable operations admin
function operationsAdmin() external view returns (address);
/// @notice Protocol treasury
/// @return The address of the treasury
function treasury() external view returns (address);
/// @notice Vault factory
/// @return The address of the vault factory
function vaultFactory() external view returns (address);
/// @notice Rewards distributor factory
/// @return The address of the rewards factory
function rewardsFactory() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Pausable
struct PausableStorage {
bool _paused;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;
function _getPausableStorage() private pure returns (PausableStorage storage $) {
assembly {
$.slot := PausableStorageLocation
}
}
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal onlyInitializing {
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
PausableStorage storage $ = _getPausableStorage();
return $._paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
import {IAccountableStrategy, Loan, DVNProof, LoanState} from "../../interfaces/IAccountableStrategy.sol";
/// @title StrategyStorage
/// @notice Storage layout for AccountableStrategy with upgrade safety gap
/// @dev Centralizes storage to avoid collisions and simplify upgradeable contract development
/// All storage variables MUST be declared here
abstract contract StrategyStorage is IAccountableStrategy {
/// @dev whether paused by admin
bool internal _pausedByAdmin;
/// @dev loan terms and state
Loan internal _loan;
/// @dev DVN proof
DVNProof internal _dvnProof;
/// @dev timestamp when the default is valid
uint256 internal _defaultValidAt;
/// @dev whether penalties are enabled
bool public penaltiesEnabled;
/// @dev whether security admin is enabled
bool public securityAdminEnabled;
/// @dev whether operations admin is enabled
bool public operationsAdminEnabled;
/// @notice loan borrower
address public borrower;
/// @notice pending loan borrower
address public pendingBorrower;
/// @dev loan state
LoanState public loanState;
/// @notice address of global registry
address public globals;
/// @notice address of fee manager
address public feeManager;
/// @notice address of safety module
address public safetyModule;
/// @notice address of proof signer
/// @dev This is the public key of the DVN proof signer
/// It can be used to verify the published DVN proofs
address public proofSigner;
/// @notice address of vault
address public vault;
/// @notice address of investment manager
address public investmentManager;
/// @notice address of rewards distributor
address public rewards;
/// @notice address of price oracle
address public priceOracle;
/// @dev Gap for future storage variables in base contract
/// @dev Reserves 50 slots for future additions to AccountableStrategy
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.20;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
pragma solidity ^0.8.20;
import {IBeacon} from "../beacon/IBeacon.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*/
library ERC1967Utils {
// We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
// This will be fixed in Solidity 0.8.21. At that point we should remove these events.
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev The `implementation` of the proxy is invalid.
*/
error ERC1967InvalidImplementation(address implementation);
/**
* @dev The `admin` of the proxy is invalid.
*/
error ERC1967InvalidAdmin(address admin);
/**
* @dev The `beacon` of the proxy is invalid.
*/
error ERC1967InvalidBeacon(address beacon);
/**
* @dev An upgrade function sees `msg.value > 0` that may be lost.
*/
error ERC1967NonPayable();
/**
* @dev Returns the current implementation address.
*/
function getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
if (newImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(newImplementation);
}
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Performs implementation upgrade with additional setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
if (data.length > 0) {
Address.functionDelegateCall(newImplementation, data);
} else {
_checkNonPayable();
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
if (newAdmin == address(0)) {
revert ERC1967InvalidAdmin(address(0));
}
StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {IERC1967-AdminChanged} event.
*/
function changeAdmin(address newAdmin) internal {
emit AdminChanged(getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
if (newBeacon.code.length == 0) {
revert ERC1967InvalidBeacon(newBeacon);
}
StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
address beaconImplementation = IBeacon(newBeacon).implementation();
if (beaconImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(beaconImplementation);
}
}
/**
* @dev Change the beacon and trigger a setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-BeaconUpgraded} event.
*
* CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
* it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
* efficiency.
*/
function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
} else {
_checkNonPayable();
}
}
/**
* @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
* if an upgrade doesn't perform an initialization call.
*/
function _checkNonPayable() private {
if (msg.value > 0) {
revert ERC1967NonPayable();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @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 ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
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: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.20;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {UpgradeableBeacon} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}{
"remappings": [
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"murky/=lib/murky/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
"solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AcceptGracePeriodTooLong","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"CapacityTooLow","type":"error"},{"inputs":[],"name":"DepositNotAllowed","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExceedsMaxDeposit","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InsufficientAmount","type":"error"},{"inputs":[],"name":"InsufficientShares","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidIntervalDurationPair","type":"error"},{"inputs":[],"name":"InvalidIntervalLength","type":"error"},{"inputs":[],"name":"InvalidPendingBorrower","type":"error"},{"inputs":[],"name":"LoanCannotBeRepaid","type":"error"},{"inputs":[],"name":"LoanNotInDefault","type":"error"},{"inputs":[],"name":"LoanNotMatured","type":"error"},{"inputs":[],"name":"LoanNotOngoing","type":"error"},{"inputs":[],"name":"LoanOngoing","type":"error"},{"inputs":[],"name":"LoanTermsAlreadySet","type":"error"},{"inputs":[],"name":"LoanTermsNotMet","type":"error"},{"inputs":[],"name":"LoanTermsNotSet","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[],"name":"NoPaymentDue","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"OutstandingInterestPayments","type":"error"},{"inputs":[],"name":"RedeemNotAllowed","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"ThresholdTooHigh","type":"error"},{"inputs":[],"name":"TimelockNotExpired","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"Borrowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldBorrower","type":"address"},{"indexed":true,"internalType":"address","name":"newBorrower","type":"address"}],"name":"BorrowerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"root","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"signature","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"messageHash","type":"bytes32"}],"name":"DVNProofPublished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"safetyModule","type":"address"},{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateral","type":"uint256"}],"name":"DefaultCovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimedInterest","type":"uint256"}],"name":"InterestClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"totalAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestPenalty","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"performanceFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"establishmentFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paymentInterval","type":"uint256"}],"name":"InterestPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"investmentManager","type":"address"}],"name":"InvestmentManagerSet","type":"event"},{"anonymous":false,"inputs":[],"name":"LoanAccepted","type":"event"},{"anonymous":false,"inputs":[],"name":"LoanDefaultInitiated","type":"event"},{"anonymous":false,"inputs":[],"name":"LoanDefaultRejected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"principal","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateral","type":"uint256"}],"name":"LoanDefaulted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingInterest","type":"uint256"}],"name":"LoanPrepaid","type":"event"},{"anonymous":false,"inputs":[],"name":"LoanRejected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"LoanRepaid","type":"event"},{"anonymous":false,"inputs":[],"name":"LoanRolloverExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"proposalId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"extensionDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"extensionNonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timeLock","type":"uint256"}],"name":"LoanRolloverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minCapacity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxCapacity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lateInterestPenalty","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestInterval","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"}],"name":"LoanTermsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"OperationsAdminEnabledSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"PenaltiesEnabledSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingBorrower","type":"address"}],"name":"PendingBorrowerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"priceOracle","type":"address"}],"name":"PriceOracleSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"proofSigner","type":"address"}],"name":"ProofSignerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"ReserveThresholdSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rewardsDistributor","type":"address"}],"name":"RewardsDistributorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"safetyModule","type":"address"}],"name":"SafetyModuleSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"SecurityAdminEnabledSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"BASIS_POINTS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DAYS_1_SECONDS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DAYS_365_SECONDS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptBorrowerRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptDefault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptLoanDynamic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptLoanLocked","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"share","type":"address"}],"name":"accruedAssets","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"authOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrower","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimInterest","outputs":[{"internalType":"uint256","name":"claimedInterest","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"coverDefault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dvnProof","outputs":[{"components":[{"internalType":"bytes32","name":"root","type":"bytes32"},{"internalType":"bytes32","name":"signature","type":"bytes32"},{"internalType":"bytes32","name":"messageHash","type":"bytes32"}],"internalType":"struct DVNProof","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globals","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"globals","type":"address"},{"internalType":"address","name":"feeManager","type":"address"},{"internalType":"address","name":"investmentManager","type":"address"},{"internalType":"enum PermissionLevel","name":"permissionLevel","type":"uint8"},{"internalType":"bool","name":"sharesTransferable","type":"bool"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"}],"internalType":"struct FixedTermInitParams","name":"params","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"interestData","outputs":[{"internalType":"uint256","name":"interest","type":"uint256"},{"internalType":"uint256","name":"penalties","type":"uint256"},{"internalType":"uint256","name":"performanceFee","type":"uint256"},{"internalType":"uint256","name":"establishmentFee","type":"uint256"},{"internalType":"uint256","name":"currentInterval","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"investmentManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"loan","outputs":[{"components":[{"internalType":"uint256","name":"minDeposit","type":"uint256"},{"internalType":"uint256","name":"minRedeem","type":"uint256"},{"internalType":"uint256","name":"maxCapacity","type":"uint256"},{"internalType":"uint256","name":"minCapacity","type":"uint256"},{"internalType":"uint256","name":"reserveThreshold","type":"uint256"},{"internalType":"uint256","name":"outstandingPrincipal","type":"uint256"},{"internalType":"uint256","name":"outstandingInterest","type":"uint256"},{"internalType":"uint256","name":"drawableFunds","type":"uint256"},{"internalType":"uint256","name":"interestRate","type":"uint256"},{"internalType":"uint256","name":"lateInterestPenalty","type":"uint256"},{"internalType":"uint256","name":"claimableInterest","type":"uint256"},{"internalType":"uint256","name":"interestInterval","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"termsSetTime","type":"uint256"},{"internalType":"uint256","name":"termsUpdateTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"depositPeriod","type":"uint256"},{"internalType":"uint256","name":"acceptGracePeriod","type":"uint256"},{"internalType":"uint256","name":"withdrawalPeriod","type":"uint256"},{"internalType":"uint256","name":"lateInterestGracePeriod","type":"uint256"}],"internalType":"struct Loan","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"loanState","outputs":[{"internalType":"enum LoanState","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"maxAssets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"maxShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"onCancelDepositRequest","outputs":[{"internalType":"bool","name":"canCancel","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"onCancelRedeemRequest","outputs":[{"internalType":"bool","name":"canCancel","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"share","type":"address"},{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"onDeposit","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"onFeeStructureChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"share","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"onMint","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"onRedeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"share","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"onRequestDeposit","outputs":[{"internalType":"bool","name":"canFulfill","type":"bool"},{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"share","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"onRequestRedeem","outputs":[{"internalType":"bool","name":"canFulfill","type":"bool"},{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"onTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"onWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"operationsAdminEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"pay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"penaltiesEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingBorrower","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"prepay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"priceOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proofSigner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"root","type":"bytes32"},{"internalType":"bytes32","name":"signature","type":"bytes32"},{"internalType":"bytes32","name":"messageHash","type":"bytes32"}],"internalType":"struct DVNProof","name":"proof","type":"tuple"}],"name":"publishDVNProof","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reject","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rejectDefault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"repay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewards","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"safetyModule","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"securityAdminEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"authSigner_","type":"address"}],"name":"setAuthSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"investmentManager_","type":"address"}],"name":"setInvestmentManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"lenders","type":"address[]"},{"internalType":"bool[]","name":"allowed","type":"bool[]"}],"name":"setLenders","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setOperationsAdminEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setPenaltiesEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newBorrower","type":"address"}],"name":"setPendingBorrower","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"priceOracle_","type":"address"}],"name":"setPriceOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"proofSigner_","type":"address"}],"name":"setProofSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"setReserveThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewards_","type":"address"}],"name":"setRewardsDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"safetyModule_","type":"address"}],"name":"setSafetyModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setSecurityAdminEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"transferable","type":"bool"}],"name":"setSharesTransferable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"minDeposit","type":"uint256"},{"internalType":"uint256","name":"minRedeem","type":"uint256"},{"internalType":"uint256","name":"maxCapacity","type":"uint256"},{"internalType":"uint256","name":"minCapacity","type":"uint256"},{"internalType":"uint256","name":"interestRate","type":"uint256"},{"internalType":"uint256","name":"lateInterestPenalty","type":"uint256"},{"internalType":"uint256","name":"lateInterestGracePeriod","type":"uint256"},{"internalType":"uint256","name":"interestInterval","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"depositPeriod","type":"uint256"},{"internalType":"uint256","name":"acceptGracePeriod","type":"uint256"},{"internalType":"uint256","name":"withdrawalPeriod","type":"uint256"}],"internalType":"struct LoanTerms","name":"terms","type":"tuple"}],"name":"setTerms","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"share","type":"address"}],"name":"sharePrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"timeDelinquent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"minDeposit","type":"uint256"},{"internalType":"uint256","name":"minRedeem","type":"uint256"},{"internalType":"uint256","name":"maxCapacity","type":"uint256"},{"internalType":"uint256","name":"minCapacity","type":"uint256"},{"internalType":"uint256","name":"interestRate","type":"uint256"},{"internalType":"uint256","name":"lateInterestPenalty","type":"uint256"},{"internalType":"uint256","name":"lateInterestGracePeriod","type":"uint256"},{"internalType":"uint256","name":"interestInterval","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"depositPeriod","type":"uint256"},{"internalType":"uint256","name":"acceptGracePeriod","type":"uint256"},{"internalType":"uint256","name":"withdrawalPeriod","type":"uint256"}],"internalType":"struct LoanTerms","name":"terms","type":"tuple"}],"name":"updateTerms","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60a080604052346100c257306080525f5160206146555f395f51905f525460ff8160401c166100b3576002600160401b03196001600160401b03821601610060575b60405161458e90816100c7823960805181818161219301526122370152f35b6001600160401b0319166001600160401b039081175f5160206146555f395f51905f525581527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80610041565b63f92ee8a960e01b5f5260045ffd5b5f80fdfe6080806040526004361015610012575f80fd5b5f905f3560e01c90816301ffc9a71461312057508063021bd7f71461306457806304fa97f014612f665780630532bca114612ec657806305dd7ac014612ea05780630987df0314612e4257806313b5573214612da55780631f3f19ab14612d3157806323b5735914612cc657806324a16c6114612c5057806325af34cd14612c215780632630c12f14612bf8578063296404f314612b875780632ca6be1f14612b1457806334fe1d1e1461294d57806335981fd8146128ee578063367b8054146128cf578063371fd8e61461276257806338d52e0f146127395780633b71be59146126f35780633f3cfc93146126d55780633f4ba83a1461256a5780634277bc19146124f65780634b31bb10146124cf5780634dc415de146124525780634f1ef286146121e757806352d1902d14612180578063530e784f1461210457806354fd4d50146120e8578063564490c4146120bf5780635c975abb146120905780635f562e4e14612024578063649f30291461200957806364a03663146105dc5780636b27733214611fcc5780636de5de0f14611f3b578063700f500614611f125780637380e4e514611e535780637601687014611e255780637df1f1b914611df85780638304ff8f14611dd05780638456cb5914611c5a578063883b755a146116e057806388e22f7f146116bd57806390d1dd7e1461164a57806391c6c0fe146115d657806392fc97ff1461157057806395b734fb14611541578063980e16e6146112e05780639ab6f6671461129a5780639b9b94211461126d5780639ec5a89414611244578063a3268b6d146111f6578063a412eeaf14611156578063a79efc80146108f9578063a864bc73146110a7578063aaf5eb681461107e578063ad3cb1cc14611031578063af19b59814610fb0578063b0f27edd14610e9a578063b2fff60314610e74578063b744199314610d23578063bc2d959814610cbc578063bef3a06f14610c93578063c290d69114610a0d578063c3124525146109e4578063c5ebeaec14610942578063c69bbd72146108f9578063d0fb0203146108d0578063d1f5c33b1461084d578063d285b7b41461061c578063d34cf335146105dc578063e1f1c4a7146105be578063e2dedd1a146104e6578063f778df8f1461038d5763fbfa77cf14610362575f80fd5b3461038a578060031936011261038a57601f546040516001600160a01b039091168152602090f35b80fd5b503461038a57602036600319011261038a57601d54600435906001600160a01b031680156104c4576020546103cd916001600160a01b0390911690613ffe565b60ff601a5460a01c1660088110156104b0576006036104a157601a805460ff60a01b1916600760a01b179055601f5482906001600160a01b0316803b15610492576040516326535a8160e01b8152600481018490523360248201529082908290604490829084905af180156104965761047d575b505060018060a01b03601d54166040519182527f92b304df396d553e0daf2f1214b48adf12a57efbf80bd482516eb565b53a852360203393a380f35b816104879161320a565b61049257815f610441565b5080fd5b6040513d84823e3d90fd5b634df5aae760e11b8252600482fd5b634e487b7160e01b83526021600452602483fd5b506020546001600160a01b031633146103cd575b6282b42960e81b8252600482fd5b503461038a576104f5366132fd565b601f54909493506001600160a01b0316330390506104d857610515613588565b60ff601a5460a01c16600881101590816105aa576007811415918261059c575b8261058a575b505061057b576002541161056c57610554604092613d0b565b5061055d61370c565b82519150600182526020820152f35b633999656760e01b8152600490fd5b63edcb7b3760e01b8252600482fd5b9091506104b057600414155f8061053b565b506005811415915083610535565b634e487b7160e01b84526021600452602484fd5b503461038a578060031936011261038a576020604051620f42408152f35b503461038a576105eb366132fd565b5050601f546001600160a01b03163303915061060e90505761060b613588565b80f35b6282b42960e81b8152600490fd5b503461038a578060031936011261038a5761026060405161063c816131d2565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201528261010082015282610120820152826101408201528261016082015282610180820152826101a0820152826101c0820152826101e082015282610200820152826102208201528261024082015201526102806040516106c7816131d2565b61026060015491828152600254602082015260035460408201526004546060820152600554608082015260065460a082015260075460c082015260085460e0820152600954610100820152600a54610120820152600b54610140820152600c54610160820152600d54610180820152600e546101a0820152600f546101c08201526010546101e0820152601154610200820152601254610220820152601354610240820152601454828201526040519283526020810151602084015260408101516040840152606081015160608401526080810151608084015260a081015160a084015260c081015160c084015260e081015160e08401526101008101516101008401526101208101516101208401526101408101516101408401526101608101516101608401526101808101516101808401526101a08101516101a08401526101c08101516101c08401526101e08101516101e08401526102008101516102008401526102208101516102208401526102408101516102408401520151610260820152f35b503461038a57602036600319011261038a576108676132bd565b6020546001600160a01b031633036104d8576001600160a01b031680156108c157602180546001600160a01b031916821790557f950a88e6770f863d57a76f0536e93cc7c318c1188c4c189aa9d7a98a6e62f8648280a280f35b63d92e233d60e01b8252600482fd5b503461038a578060031936011261038a57601c546040516001600160a01b039091168152602090f35b503461038a57604036600319011261038a576109136132bd565b5061091c6132d3565b50601f546001600160a01b0316330361060e57610937613588565b602060405160018152f35b503461038a57602036600319011261038a576019546004359060181c6001600160a01b031633036104d857610975613588565b61097d6138fc565b61098561419a565b90808210156109dd57505b80156109ce5761099f8161421b565b6040519081527fac59582e5396aca512fa873a2047e7f4c80f8f55d4a06cb34a78a0187f62719f60203392a280f35b632ca2f52b60e11b8252600482fd5b9050610990565b503461038a578060031936011261038a57601b546040516001600160a01b039091168152602090f35b503461038a57602036600319011261038a5760195460181c6001600160a01b0316338190036104d857610a3e613588565b610a466138fc565b610a56610a51613dca565b613e8a565b9394610a65838795949561344b565b95610a7984610a74858461342a565b61342a565b968715610c84578760043510610c7557426058556059879055601f546040516318160ddd60e01b81528a946001600160a01b03909216939291602082600481885afa938415610c6a5788948c938891610c20575b5091610b0b859492610af1610ae9610a7496610b159a996139f0565b605a5461342a565b605a55610b0086600b5461342a565b600b5560075461344b565b6007553090613c64565b90803b15610c07576040516326535a8160e01b8152600481019290925230602483015282908290604490829084905af1801561049657610c0b575b50601c54605d546001600160a01b039182169116813b15610c0757829160648392604051948593849263acc10f1160e01b845260048401528a60248401528860448401525af1801561049657610bee575b5050604080519586526020860192909252908401919091526060830152608082015233907f62822f533950622f8ee70b321a9d4bced56e38f52fd791b8be97e09086cd68af9060a090a280f35b81610bf89161320a565b610c0357855f610ba1565b8580fd5b8280fd5b81610c159161320a565b610c0357855f610b50565b97505093509190506020853d602011610c62575b81610c416020938361320a565b81010312610c5e5793518a94879390928b92909190610b0b610acd565b5f80fd5b3d9150610c34565b6040513d88823e3d90fd5b632ca2f52b60e11b8952600489fd5b63589b68c960e01b8952600489fd5b503461038a578060031936011261038a57601d546040516001600160a01b039091168152602090f35b503461038a57602036600319011261038a57610cd66132bd565b601f546001600160a01b031633036104d8576020546001600160a01b0391821691168114159081610d0a575b5061060e5780f35b60195460181c6001600160a01b0316141590505f610d02565b503461038a57604036600319011261038a576004356001600160401b03811161049257610d549036906004016133fa565b6024929192356001600160401b038111610c0757610d769036906004016133fa565b6020546001600160a01b03163303610e6657601f546001600160a01b031694853b15610e62579260405193630eaec18160e21b855281604486016040600488015252606485019091865b818110610e3657505084810360031901602486015282815260200192919050845b818110610e1557505050818394818581819503925af1801561049657610e045750f35b81610e0e9161320a565b61038a5780f35b909192602080600192610e278761335a565b15158152019401929101610de1565b909160019060209081906001600160a01b03610e51886132e9565b168152019401910192919092610dc0565b8480fd5b6282b42960e81b8452600484fd5b503461038a578060031936011261038a57602060ff60195460081c166040519015158152f35b503461038a578060031936011261038a57601854804210908115610fa7575b50610f9857601a805460ff60a01b1916600360a11b179055600654601f546040516278744560e21b815290602090829060049082906001600160a01b03165afa908115610f8d578391610f3b575b507ff5c2e2ff5a0aa88c6e9352be55b58e2c44042366ea1a2e7a63b3b8ad3ef36c499160409182519182526020820152a180f35b90506020813d602011610f85575b81610f566020938361320a565b81010312610c5e57517ff5c2e2ff5a0aa88c6e9352be55b58e2c44042366ea1a2e7a63b3b8ad3ef36c49610f07565b3d9150610f49565b6040513d85823e3d90fd5b63621e25c360e01b8152600490fd5b9050155f610eb9565b503461038a57606036600319011261038a57604051610fce816131a3565b6004358152602081019060243582526040810190604435825260018060a01b03601e54163303610e66575191826015555190816016555191826017557f4cf2ac0ad075ffb506f42eb2a06e69cc4204d34ae016ec7fb8175deb07fc312d8480a480f35b503461038a578060031936011261038a575061107a60405161105460408261320a565b60058152640352e302e360dc1b60208201526040519182916020835260208301906133d6565b0390f35b503461038a578060031936011261038a5760206040516a0c097ce7bc90715b34b9f160241b8152f35b503461038a578060031936011261038a57601d546001600160a01b03168015611135576020546110e2916001600160a01b0390911690613ffe565b6110ea6138fc565b620151804201804211611121576018557f213beaf8edb2ee3432f9a9bf220091337cc7ad2255d055b87295c358927732758180a180f35b634e487b7160e01b82526011600452602482fd5b506020546001600160a01b031633146110e2576282b42960e81b8152600490fd5b503461038a578060031936011261038a57601a546001600160a01b03811633036111e757601980546301000000600160b81b0319811633601881811b6301000000600160b81b0316929092179093556001600160a01b0319909316601a5590911c6001600160a01b03167f7d35bbbf65726702b94fcdce7962d3ed40813b4e323b3dce330b4f39a9b5678e8380a380f35b637b96997560e01b8252600482fd5b503461038a578060031936011261038a576020546001600160a01b0316330361060e57806018557f2446e665433d1b4519764cc05a1376b8b2356182dafd4c83b63b414d6d0877a98180a180f35b503461038a578060031936011261038a576021546040516001600160a01b039091168152602090f35b503461038a57602036600319011261038a576112876132bd565b50602061129261370c565b604051908152f35b503461038a576112a9366132fd565b5050601f546001600160a01b03163303915061060e9050576040906112cc613588565b6112d461370c565b82519182526020820152f35b503461038a576112ef3661322b565b6020546001600160a01b031633036104d85760ff601a5460a01c1660088110156104b057611532576101008101805160e083019081519081156115235781811061152357066115145760195460181c6001600160a01b03161561150557839060c084015180155f146114ff57506202a300925b610140850151806114f957506202a300945b6101208101519461016082015160a083019788519786519860808601998a519187519260208901519b60408a019c8d519a6060019a8b51918d51978c6040516113bc816131d2565b8281528360208201528460408201528560608201528660808201528660a08201528660c08201528660e08201528761010082015288610120820152866101408201528961016082015286610180820152866101a0820152866101c08201528a6101e08201528b6102008201528c6102208201528d6102408201526102600152600155600255600355600455806005558060065580600755600855600955600a558c600b55600c558b600d558b600e558b600f5560105560115560125560135560145561148661406b565b601a805460ff60a01b1916600160a01b17905542600e555192519351945191519051604080519485526020850195909552938301949094526060820152608081019290925260a08201527f1908367aaa484e6384b9bfcacded491c394fcc26a1e4371cad1497b2ab32f0d29060c090a180f35b94611374565b92611362565b63d92e233d60e01b8452600484fd5b6344d080eb60e11b8452600484fd5b6328df285560e01b8652600486fd5b63166b30ff60e11b8252600482fd5b503461038a57604036600319011261038a5761155b6132bd565b506115646132d3565b5060206040515f198152f35b503461038a578060031936011261038a576040805161158e816131a3565b828152826020820152015260606040516115a7816131a3565b601554908181526016546020820190815260406017549201918252604051928352516020830152516040820152f35b503461038a57602036600319011261038a576115f06132bd565b6020546001600160a01b031633036104d8576001600160a01b031680156108c157601e80546001600160a01b031916821790557fbb75484da0d5291c0ccc8e65abd3d469e055aa189f31c7bd824ada5936ebfbbd8280a280f35b503461038a57602036600319011261038a5761166461334b565b6020546001600160a01b031633036104d85760207f641a92ef68a52d8da528c56ccfda46f5e141262f154a6a8c075306fdea6d2b5291151560195462ff00008260101b169062ff0000191617601955604051908152a180f35b503461038a578060031936011261038a57602060ff601954166040519015158152f35b503461038a57602036600319011261038a57600435906001600160401b03821161038a57610100600319833603011261038a5760405161010081018181106001600160401b03821117611c465760405261173c836004016132e9565b815261174a602484016132e9565b906020810191825261175e604485016132e9565b60408201908152611771606486016132e9565b916060810192835260848601356003811015610c03576080820190815261179a60a4880161335a565b60a0830190815260c48801356001600160401b038111611c42576117c490600436918b01016133b8565b9760c0840198895260e48101356001600160401b038111611c3e576117ee913691016004016133b8565b9460e084019586525f5160206145395f395f51905f52549660ff8860401c1615976001600160401b03811680159081611c36575b6001149081611c2c575b159081611c23575b50611c145767ffffffffffffffff1981166001175f5160206145395f395f51905f525588611be8575b50611866614440565b61186e614440565b60ff195f5160206145195f395f51905f5254165f5160206145195f395f51905f5255611898614440565b84516001600160a01b031615611bd95780516001600160a01b031615611bd95785516001600160a01b031615611bd95781516001600160a01b031615611bd9578451605d80546001600160a01b03199081166001600160a01b039384169081179092559251601b805485169184169190911790558751601c80548516918416919091179055601a805460ff60a01b1916905592516020805490931691161790556019805460ff191660011790559051915191151591906003821015611bc5578798979697519551926004602060018060a01b03601b54166040519283809263d8a06f7360e01b82525afa928315611bba576020958a928395611b97575b506119e9906119d76040519b8c9889978896639fd29a9360e01b885260048801523060248801526044870152606486015260e0608486015260e48501906133d6565b8381036003190160a4850152906133d6565b6a0c097ce7bc90715b34b9f160241b60c483015203926001600160a01b03165af1928315611b5f5784936020918591611b6a575b50601f80546001600160a01b0319166001600160a01b03928316908117909155835160405163095ea7b360e01b815260048101929092525f19602483015290958692604492849291165af1908115611b5f57602093604492611b44575b5051915160405163095ea7b360e01b81526001600160a01b0391821660048201525f1960248201529485938492165af18015610f8d57611b15575b50611abd5780f35b68ff0000000000000000195f5160206145395f395f51905f5254165f5160206145395f395f51905f52557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a180f35b611b369060203d602011611b3d575b611b2e818361320a565b810190613570565b505f611ab5565b503d611b24565b611b5a90853d8711611b3d57611b2e818361320a565b611a7a565b6040513d86823e3d90fd5b611b8a9150823d8411611b90575b611b82818361320a565b8101906134c3565b5f611a1d565b503d611b78565b6119e9919550611bb390883d8a11611b9057611b82818361320a565b9490611995565b6040513d8b823e3d90fd5b634e487b7160e01b88526021600452602488fd5b63d92e233d60e01b8952600489fd5b68ffffffffffffffffff191668010000000000000001175f5160206145395f395f51905f52555f61185d565b63f92ee8a960e01b8a5260048afd5b9050155f611834565b303b15915061182c565b8a9150611822565b8880fd5b8780fd5b634e487b7160e01b83526041600452602483fd5b503461038a578060031936011261038a5760195460081c60ff1615611db057601b5460405163be7c13f760e01b815290602090829060049082906001600160a01b03165afa801561049657611cc3918391611d91575b506020546001600160a01b031690613ffe565b601b5460405163be7c13f760e01b815290602090829060049082906001600160a01b03165afa908115610496578291611d72575b506001600160a01b03163314611d62575b611d10613588565b600160ff195f5160206145195f395f51905f525416175f5160206145195f395f51905f52557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a180f35b805460ff19166001178155611d08565b611d8b915060203d602011611b9057611b82818361320a565b5f611cf7565b611daa915060203d602011611b9057611b82818361320a565b5f611cb0565b6020546001600160a01b03163314611cc3576282b42960e81b8152600490fd5b503461038a57602036600319011261038a57611dea6132bd565b506020600b54604051908152f35b503461038a578060031936011261038a5760195460405160189190911c6001600160a01b03168152602090f35b503461038a57604036600319011261038a57611e3f6132bd565b50611e486132d3565b50602061129261417f565b503461038a578060031936011261038a5760195460181c6001600160a01b0316330361060e57611e81613588565b611e896135af565b611e9161402c565b611ea06006546008549061342a565b60045411611f0357611eb061406b565b611eba6002613458565b42600d55611ec661419a565b80611ef4575b507fa222645ee08a6672a8a0b45153fe630c9bb71dbd07af71b39941e29f8bd428768180a180f35b611efd9061421b565b5f611ecc565b634075283f60e01b8152600490fd5b503461038a578060031936011261038a57601a546040516001600160a01b039091168152602090f35b503461038a57602036600319011261038a57611f556132bd565b602054906001600160a01b0382163303611fbe576001600160a01b0316908115611faf576001600160a01b03191681176020557f16bd984efd11b8bbf2957266a55bcb3b3721d05b70044d559f0c58d80f8bd36c8280a280f35b63d92e233d60e01b8352600483fd5b6282b42960e81b8352600483fd5b503461038a578060031936011261038a57611fe5613588565b601c546001600160a01b0316330361060e57600e546120015780f35b61060b61406b565b503461038a578060031936011261038a576020611292613513565b503461038a57612033366132fd565b50601f549193909250906001600160a01b0316330361060e575061207e6120869261205c613588565b6120646136cc565b61206d816138c5565b612076816138de565b60085461342a565b6008556135d9565b602061129261370c565b503461038a578060031936011261038a57602060ff5f5160206145195f395f51905f5254166040519015158152f35b503461038a578060031936011261038a57601e546040516001600160a01b039091168152602090f35b503461038a578060031936011261038a57602060405160018152f35b503461038a57602036600319011261038a5761211e6132bd565b6020546001600160a01b031633036104d85761213861402c565b6001600160a01b031680156108c157602280546001600160a01b031916821790557f6536690106168bdf4ba72c128a053d817999b1db90cae23f139b293bf862cb758280a280f35b503461038a578060031936011261038a577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031630036121d85760206040515f5160206144f95f395f51905f528152f35b63703e46dd60e11b8152600490fd5b50604036600319011261038a576121fc6132bd565b906024356001600160401b03811161049257366023820112156104925761222d903690602481600401359101613382565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016308114908115612430575b506124215760195460081c60ff161561240157601b5460405163be7c13f760e01b815290602090829060049082906001600160a01b03165afa908115610f8d57906122c0918491611d9157506020546001600160a01b031690613ffe565b6040516352d1902d60e01b8152926001600160a01b0381169190602085600481865afa809585966123cd575b5061230557634c9c8ce360e01b84526004839052602484fd5b9091845f5160206144f95f395f51905f5281036123bb5750813b156123a9575f5160206144f95f395f51905f5280546001600160a01b031916821790557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8480a2815183901561238f578083602061238b95519101845af461238561446b565b9161449a565b5080f35b5050503461239a5780f35b63b398979f60e01b8152600490fd5b634c9c8ce360e01b8452600452602483fd5b632a87526960e21b8552600452602484fd5b9095506020813d6020116123f9575b816123e96020938361320a565b81010312610e625751945f6122ec565b3d91506123dc565b6020546001600160a01b031633146122c0576282b42960e81b8252600482fd5b63703e46dd60e11b8252600482fd5b5f5160206144f95f395f51905f52546001600160a01b0316141590505f612262565b503461038a578060031936011261038a5761246b613588565b61247361402c565b61248e612485600e546011549061342a565b6012549061342a565b421115611f0357601a805460ff60a01b1916600160a21b1790557fb9d6f6ff8a5599d0d4b1aff8493383c1a00535e647c270af1ecb4649934b4f228180a180f35b503461038a578060031936011261038a57602080546040516001600160a01b039091168152f35b503461038a57602036600319011261038a576125106132bd565b6020546001600160a01b031633036104d8576001600160a01b031680156108c157601d80546001600160a01b031916821790557f5ef7f35db90768a7ec0edc8fcf3bdccbd2b68543ec71389db2d555ff07b282a58280a280f35b503461038a578060031936011261038a5760195460081c60ff16156126b557601b5460405163be7c13f760e01b815290602090829060049082906001600160a01b03165afa8015610496576125d2918391611d9157506020546001600160a01b031690613ffe565b805460ff81168061264f575b6104d85760ff191681555f5160206145195f395f51905f525460ff8116156126405760ff19165f5160206145195f395f51905f52557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a180f35b638dfc202b60e01b8252600482fd5b50601b5460405163be7c13f760e01b815290602090829060049082906001600160a01b03165afa908115610f8d578391612696575b506001600160a01b03163314156125de565b6126af915060203d602011611b9057611b82818361320a565b5f612684565b6020546001600160a01b031633146125d2576282b42960e81b8152600490fd5b503461038a578060031936011261038a576020604051620151808152f35b503461038a578060031936011261038a5761107a61270f613494565b604080519586526020860194909452928401919091526060830152608082015290819060a0820190565b503461038a578060031936011261038a57605d546040516001600160a01b039091168152602090f35b503461038a57602036600319011261038a5760195460181c6001600160a01b0316330361060e57612791613588565b6127a0600d546010549061342a565b42108061289f575b612890576127b46138fc565b600754612881576006805490829055601a805460ff60a01b1916600560a01b1790556019546127f2908290309060181c6001600160a01b0316613c64565b601f5482906001600160a01b0316803b15610492576040516326535a8160e01b8152600481018490523060248201529082908290818381604481015b03925af180156104965761286c575b507f9a7851747cd7ffb3fe0a32caf3da48b31f27cebe131267051640f8b72fc47186602083604051908152a180f35b816128769161320a565b61049257815f61283d565b631ee1e36d60e21b8152600490fd5b63f37d71eb60e01b8152600490fd5b5060ff601a5460a01c1660088110156128bb57600514156127a8565b634e487b7160e01b82526021600452602482fd5b503461038a578060031936011261038a5760206040516301e133808152f35b503461038a578060031936011261038a57612907613588565b61290f6138fc565b602061291a33613d0b565b6040518181527ff9984c8173c4b4ff9cc454f76ed19c053f4490ccb224fd9ddffeb4ef4bea3530833392a2604051908152f35b503461038a578060031936011261038a5760195460181c6001600160a01b0316330361060e5761297b613588565b6129836138fc565b612995600d54600c546057549161393c565b80612aaf575b50600654601c54604051635f72b8c160e01b81523060048201528391602090829060249082906001600160a01b03165afa908115610496578291612a7a575b508015612a70576129ee612a16918461396d565b600683905560078390556008839055601a805460ff60a01b1916600560a01b1790558361342a565b601954612a32908290309060181c6001600160a01b0316613c64565b601f546001600160a01b0316803b15610c07576040516326535a8160e01b81526004810192909252306024830152829082908183816044810161282e565b50612a16816129ee565b9150506020813d602011612aa7575b81612a966020938361320a565b81010312610c5e578290515f6129da565b3d9150612a89565b605954905f198101818111612b00578210612af1576001149081612ae8575b50612ad9575f61299b565b635f73a3b760e01b8152600490fd5b9050155f612ace565b635f73a3b760e01b8352600483fd5b634e487b7160e01b84526011600452602484fd5b503461038a57602036600319011261038a57602054600435906001600160a01b031633036104d857620f42408111612b78576020817f9530355cac0969404662db60e90664d90fdf47c37c69fa53616fc509fde4c29492600555604051908152a180f35b63e56d58cf60e01b8252600482fd5b503461038a57602036600319011261038a57612ba161334b565b6020546001600160a01b031633036104d85760207f692eab81334fcd354cab995569c5a935a76a8abeca88863819ab4e5c78e1d16191151560195461ff008260081b169061ff00191617601955604051908152a180f35b503461038a578060031936011261038a576022546040516001600160a01b039091168152602090f35b503461038a578060031936011261038a5760ff601a5460a01c166040519060088110156104b057602092508152f35b503461038a57602036600319011261038a57612c6a61334b565b6020546001600160a01b031633036104d857601f5482916001600160a01b0390911690813b15612cc25782916024839260405194859384926324a16c6160e01b8452151560048401525af1801561049657610e045750f35b5050fd5b503461038a57602036600319011261038a57612ce061334b565b6020546001600160a01b031633036104d85760207f2cbdf9efae4202902b928a7521f81e3e5253fecaf3c7a66f2bd8b337310e32b891151560ff196019541660ff821617601955604051908152a180f35b503461038a57602036600319011261038a57612d4b6132bd565b6020546001600160a01b031633036104d8576001600160a01b031680156108c157601a80546001600160a01b031916821790557f10f06072822ef73860fedb88933f968d20bb4aadce8a8d360d1124cb6ce1e0b28280a280f35b503461038a57612db4366132fd565b50601f54909392506001600160a01b031633036104d857612dd3613588565b612ddb6136cc565b612de361370c565b916a0c097ce7bc90715b34b9f160241b83612dff828286613bdf565b9309612e1b575b506020926112929161207e9061206d816138c5565b60018201809211612e2e57506020612e06565b634e487b7160e01b81526011600452602490fd5b503461038a57608036600319011261038a57612e5c6132bd565b50612e656132d3565b604435906001600160a01b0382168203610c0757601f546001600160a01b03163303611fbe5790612e9b61060b92612e9b613588565b6135d9565b503461038a578060031936011261038a57602060ff60195460101c166040519015158152f35b5034610c5e576020366003190112610c5e57612ee06132bd565b6020546001600160a01b03163303612f5857601f546001600160a01b031690813b15610c5e57604051636c19e78360e01b81526001600160a01b039091166004820152905f908290602490829084905af18015612f4d57612f3f575080f35b612f4b91505f9061320a565b005b6040513d5f823e3d90fd5b6282b42960e81b5f5260045ffd5b34610c5e57612f743661322b565b6020546001600160a01b03163303612f5857612f8e6135af565b6040810190815160035411613055576101408101805160125410613046576130417f1908367aaa484e6384b9bfcacded491c394fcc26a1e4371cad1497b2ab32f0d29351928360035560608101519283600455602082015160025581516001556101208201516011555160125542600f5560095490600a5461010060e083015192015192604051968796879260a094919796959260c0850198855260208501526040840152606083015260808201520152565b0390a1005b63a87681b160e01b5f5260045ffd5b6319cf95fb60e11b5f5260045ffd5b34610c5e575f366003190112610c5e5760195460181c6001600160a01b03163303612f5857613091613588565b6130996135af565b6130a161402c565b6130b06006546008549061342a565b60045411613111576130c061406b565b6130ca6003613458565b42600d556130d661419a565b80613102575b7fa222645ee08a6672a8a0b45153fe630c9bb71dbd07af71b39941e29f8bd428765f80a1005b61310b9061421b565b806130dc565b634075283f60e01b5f5260045ffd5b34610c5e576020366003190112610c5e576004359063ffffffff60e01b8216809203610c5e576020916351f695a360e11b8114908115613192575b8115613181575b8115613170575b5015158152f35b6301ffc9a760e01b14905083613169565b6332b6874d60e01b81149150613162565b63a04fd5db60e01b8114915061315b565b606081019081106001600160401b038211176131be57604052565b634e487b7160e01b5f52604160045260245ffd5b61028081019081106001600160401b038211176131be57604052565b61016081019081106001600160401b038211176131be57604052565b90601f801991011681019081106001600160401b038211176131be57604052565b610180906003190112610c5e5760405161018081018181106001600160401b038211176131be576040526004358152602435602082015260443560408201526064356060820152608435608082015260a43560a082015260c43560c082015260e43560e08201526101043561010082015261012435610120820152610144356101408201526101643561016082015290565b600435906001600160a01b0382168203610c5e57565b602435906001600160a01b0382168203610c5e57565b35906001600160a01b0382168203610c5e57565b6080906003190112610c5e576004356001600160a01b0381168103610c5e5790602435906044356001600160a01b0381168103610c5e57906064356001600160a01b0381168103610c5e5790565b600435908115158203610c5e57565b35908115158203610c5e57565b6001600160401b0381116131be57601f01601f191660200190565b92919261338e82613367565b9161339c604051938461320a565b829481845281830111610c5e578281602093845f960137010152565b9080601f83011215610c5e578160206133d393359101613382565b90565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9181601f84011215610c5e578235916001600160401b038311610c5e576020808501948460051b010111610c5e57565b9190820180921161343757565b634e487b7160e01b5f52601160045260245ffd5b9190820391821161343757565b600881101561348057601a805460ff60a01b191660a09290921b60ff60a01b16919091179055565b634e487b7160e01b5f52602160045260245ffd5b61349c613dca565b6020810151156134b6576134af90613e8a565b9091929394565b505f905f905f905f905f90565b90816020910312610c5e57516001600160a01b0381168103610c5e5790565b8181029291811591840414171561343757565b81156134ff570490565b634e487b7160e01b5f52601260045260245ffd5b601454605954600181018091116134375761353e90613538600d5491600c54906134e2565b9061342a565b90613549818361342a565b42116135555750505f90565b61356661356c91620151809361342a565b4261344b565b0490565b90816020910312610c5e57518015158103610c5e5790565b60ff5f5160206145195f395f51905f5254166135a057565b63d93c066560e01b5f5260045ffd5b60ff601a5460a01c166008811015613480576001036135ca57565b630d13988560e11b5f5260045ffd5b601f546040516370a0823160e01b81526001600160a01b03928316600482018190529092909160209184916024918391165afa918215612f4d575f92613698575b50605a5491801561368957815f52605b60205261363b60405f20548461344b565b908115613683576a0c097ce7bc90715b34b9f160241b9161365b916134e2565b04815f52605c60205261367360405f2091825461342a565b90555f52605b60205260405f2055565b50505050565b505f52605b60205260405f2055565b9091506020813d6020116136c4575b816136b46020938361320a565b81010312610c5e5751905f61361a565b3d91506136a7565b60ff601a5460a01c1660088110156134805760018114159081613700575b506136f157565b6301ec871560e51b5f5260045ffd5b6003915014155f6136ea565b60ff601a5460a01c16613724600e546011549061342a565b421115806138a5575b8015613892575b61387f5760088110156134805760058114908115613874575b5061380e5760565460575461376881600d54600c549061393c565b906001820180921161343757808210156137e457826a0c097ce7bc90715b34b9f160241b0192836a0c097ce7bc90715b34b9f160241b11613437576137b06137b6938361344b565b90613bdf565b6a0c097ce7bc90715b34b9f160241b01806a0c097ce7bc90715b34b9f160241b11613437576133d3916139f0565b50506a0c097ce7bc90715b34b9f160241b01806a0c097ce7bc90715b34b9f160241b116134375790565b601f54604051633c46796f60e11b815290602090829060049082906001600160a01b03165afa908115612f4d575f91613845575090565b90506020813d60201161386c575b816138606020938361320a565b81010312610c5e575190565b3d9150613853565b60079150145f61374d565b506a0c097ce7bc90715b34b9f160241b90565b5060088110156134805760048114613734565b506008811015613480576002811415801561372d5750600381141561372d565b600154116138cf57565b632ca2f52b60e11b5f5260045ffd5b6138e661417f565b106138ed57565b63e4bac01b60e01b5f5260045ffd5b60ff601a5460a01c1660088110156134805760028114159081613930575b5061392157565b639adffb1560e01b5f5260045ffd5b6003915014155f61391a565b90811561396657613950613955924261344b565b6134f5565b81811115613961575090565b905090565b5050505f90565b9190915f838202915f19858209918380841093039280840393146139e25782620f424011156139d357507fde8f6cefed634549b62c77574f722e1ac57e23f24d8fd5cb790fb65668c261399394620f4240910990828211900360fa1b910360061c170290565b63227bc15360e01b8152600490fd5b505050620f42409192500490565b906a0c097ce7bc90715b34b9f160241b8202905f196a0c097ce7bc90715b34b9f160241b840992828085109403938085039414613aa15783821115613a92576a0c097ce7bc90715b34b9f160241b829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b63227bc15360e01b5f5260045ffd5b50906133d392506134f5565b9190915f838202915f1985820991838084109303928084039314613b2957826a0c097ce7bc90715b34b9f160241b11156139d357507f356317ffa45f828d2a41175b2aed3a20e7fdd4a4e46cc11944d93b01be7b171193946a0c097ce7bc90715b34b9f160241b910990828211900360dc1b910360241c170290565b5050506a0c097ce7bc90715b34b9f160241b9192500490565b905f6a0c097ce7bc90715b34b9f160241b8302905f196a0c097ce7bc90715b34b9f160241b850990828083109203918083039214613bd257816301e1338011156139d357506301e133806a0c097ce7bc90715b34b9f160241b7f98f5be4dd1e14769fbd6666224dc1eb80dd2e0a3d2c8b328f57e76b7ae10395794950990828211900360f91b910360071c170290565b50506301e1338090049150565b9091828202915f1984820993838086109503948086039514613c575784831115613a9257829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b5050906133d392506134f5565b605d546040516323b872dd60e01b602082019081526001600160a01b03938416602483015293831660448201526064808201959095529384521691613cc8915f91829190613cb360848261320a565b519082865af1613cc161446b565b908361449a565b8051908115159182613cf0575b5050613cde5750565b635274afe760e01b5f5260045260245ffd5b613d039250602080918301019101613570565b155f80613cd5565b90613d15826135d9565b6001600160a01b0382165f818152605c602052604090205492908315613dc3575f52605c6020525f6040812055600b54838082115f14613dbb57613d589161344b565b600b55601f546001600160a01b0316803b15610c5e5760405163fb53db6d60e01b8152600481018590526001600160a01b0390921660248301525f908290818381604481015b03925af18015612f4d57613daf5750565b5f613db99161320a565b565b50505f613d58565b505f925050565b5f610140604051613dda816131ee565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e082015282610100820152826101208201520152601054600d54600c5460065460145490600a549260575460555490605854926059549460ff60195416986040519a613e4e8c6131ee565b8b5260208b015260408a01526060890152608088015260a087015260c086015260e0850152610100840152610120830152151561014082015290565b9060c08201519160208101805191613eb760e08201519560608301968751906080850196875193426142b6565b15613fdc57600754915b601c54604051631d5f24ef60e11b815230600482015293976001600160a01b03909116958894936020856024818b5afa948515612f4d575f95613fa7575b506020613f0e6024968861396d565b98604051968780926350355cb960e11b82523060048301525afa948515612f4d575f95613f6d575b5090613f55613f619392613f6a96604087015191845191885193614329565b9651915190519161393c565b8093429061439e565b93565b945091906020853d602011613f9f575b81613f8a6020938361320a565b81010312610c5e579351939091613f55613f36565b3d9150613f7d565b94506020853d602011613fd4575b81613fc26020938361320a565b81010312610c5e579351936020613eff565b3d9150613fb5565b613ff86040830151613ff28360a08601516134e2565b90613aad565b91613ec1565b6001600160a01b03163314159081614018575b50612f5857565b6001600160a01b031633141590505f614011565b60ff601a5460a01c1660088110156134805760028114908115614060575b5061405157565b63ac9d28cd60e01b5f5260045ffd5b60039150145f61404a565b601054600c54600954601c54604051631d5f24ef60e11b81523060048201529293919290602090829060249082906001600160a01b03165afa908115612f4d575f9161414d575b50620f424003620f42408111613437576140cc908361396d565b906141036140d982613b42565b946140fd6140e682613b42565b966140f7605954986058549861396d565b9561396d565b926134f5565b9160405160a081018181106001600160401b038211176131be5786916080916040528381528460208201528560408201528660608201520152605555605655605755605855605955565b90506020813d602011614177575b816141686020938361320a565b81010312610c5e57515f6140b2565b3d915061415b565b6133d36141916003546006549061344b565b6008549061344b565b600854601f54604051634f98428960e11b815290602090829060049082906001600160a01b03165afa908115612f4d575f916141e9575b50808211156141e3576133d39161344b565b50505f90565b90506020813d602011614213575b816142046020938361320a565b81010312610c5e57515f6141d1565b3d91506141f7565b6142278160085461344b565b6008556142368160065461342a565b60065561427561426d614267600d54614262600c5461425c60575480926055549561393c565b9061344b565b6134e2565b83613aad565b60075461342a565b600755601f546001600160a01b0316803b15610c5e5760405163fb53db6d60e01b815260048101929092523360248301525f90829081838160448101613d9e565b93909182156142ff5750818411156142f4576139506142d8926142de9561344b565b9261344b565b905b8181106142ee575060019091565b915f9150565b50505050505f905f90565b9384919350809250111561431f5761431a926139509161344b565b6142e0565b505050505f905f90565b929381158015614396575b61438d5761426261426292614348956134e2565b9080651cae8c13e0000290651cae8c13e0008204036134375761436d81600184613bdf565b9181156134ff576001900961437f5790565b600181018091116134375790565b50505050505f90565b508215614334565b919290610140820151156144385760e082015180941115614438575f93602083015160018201809211613437576135386143dd926060860151906134e2565b61010083016143ed81518361342a565b8311614417575b505083159050613966576133d392610120614411920151906134e2565b9061396d565b6201518093955061442e929161425c91519061342a565b04915f80806143f4565b505050505f90565b60ff5f5160206145395f395f51905f525460401c161561445c57565b631afcd79f60e31b5f5260045ffd5b3d15614495573d9061447c82613367565b9161448a604051938461320a565b82523d5f602084013e565b606090565b906144be57508051156144af57805190602001fd5b630a12f52160e11b5f5260045ffd5b815115806144ef575b6144cf575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156144c756fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbccd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220e48cdb9ac2ac6c188aaeef1e0f88b4bbdc0221247fbae542096a1b53fab78a1964736f6c634300081b0033f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00
Deployed Bytecode
0x6080806040526004361015610012575f80fd5b5f905f3560e01c90816301ffc9a71461312057508063021bd7f71461306457806304fa97f014612f665780630532bca114612ec657806305dd7ac014612ea05780630987df0314612e4257806313b5573214612da55780631f3f19ab14612d3157806323b5735914612cc657806324a16c6114612c5057806325af34cd14612c215780632630c12f14612bf8578063296404f314612b875780632ca6be1f14612b1457806334fe1d1e1461294d57806335981fd8146128ee578063367b8054146128cf578063371fd8e61461276257806338d52e0f146127395780633b71be59146126f35780633f3cfc93146126d55780633f4ba83a1461256a5780634277bc19146124f65780634b31bb10146124cf5780634dc415de146124525780634f1ef286146121e757806352d1902d14612180578063530e784f1461210457806354fd4d50146120e8578063564490c4146120bf5780635c975abb146120905780635f562e4e14612024578063649f30291461200957806364a03663146105dc5780636b27733214611fcc5780636de5de0f14611f3b578063700f500614611f125780637380e4e514611e535780637601687014611e255780637df1f1b914611df85780638304ff8f14611dd05780638456cb5914611c5a578063883b755a146116e057806388e22f7f146116bd57806390d1dd7e1461164a57806391c6c0fe146115d657806392fc97ff1461157057806395b734fb14611541578063980e16e6146112e05780639ab6f6671461129a5780639b9b94211461126d5780639ec5a89414611244578063a3268b6d146111f6578063a412eeaf14611156578063a79efc80146108f9578063a864bc73146110a7578063aaf5eb681461107e578063ad3cb1cc14611031578063af19b59814610fb0578063b0f27edd14610e9a578063b2fff60314610e74578063b744199314610d23578063bc2d959814610cbc578063bef3a06f14610c93578063c290d69114610a0d578063c3124525146109e4578063c5ebeaec14610942578063c69bbd72146108f9578063d0fb0203146108d0578063d1f5c33b1461084d578063d285b7b41461061c578063d34cf335146105dc578063e1f1c4a7146105be578063e2dedd1a146104e6578063f778df8f1461038d5763fbfa77cf14610362575f80fd5b3461038a578060031936011261038a57601f546040516001600160a01b039091168152602090f35b80fd5b503461038a57602036600319011261038a57601d54600435906001600160a01b031680156104c4576020546103cd916001600160a01b0390911690613ffe565b60ff601a5460a01c1660088110156104b0576006036104a157601a805460ff60a01b1916600760a01b179055601f5482906001600160a01b0316803b15610492576040516326535a8160e01b8152600481018490523360248201529082908290604490829084905af180156104965761047d575b505060018060a01b03601d54166040519182527f92b304df396d553e0daf2f1214b48adf12a57efbf80bd482516eb565b53a852360203393a380f35b816104879161320a565b61049257815f610441565b5080fd5b6040513d84823e3d90fd5b634df5aae760e11b8252600482fd5b634e487b7160e01b83526021600452602483fd5b506020546001600160a01b031633146103cd575b6282b42960e81b8252600482fd5b503461038a576104f5366132fd565b601f54909493506001600160a01b0316330390506104d857610515613588565b60ff601a5460a01c16600881101590816105aa576007811415918261059c575b8261058a575b505061057b576002541161056c57610554604092613d0b565b5061055d61370c565b82519150600182526020820152f35b633999656760e01b8152600490fd5b63edcb7b3760e01b8252600482fd5b9091506104b057600414155f8061053b565b506005811415915083610535565b634e487b7160e01b84526021600452602484fd5b503461038a578060031936011261038a576020604051620f42408152f35b503461038a576105eb366132fd565b5050601f546001600160a01b03163303915061060e90505761060b613588565b80f35b6282b42960e81b8152600490fd5b503461038a578060031936011261038a5761026060405161063c816131d2565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201528261010082015282610120820152826101408201528261016082015282610180820152826101a0820152826101c0820152826101e082015282610200820152826102208201528261024082015201526102806040516106c7816131d2565b61026060015491828152600254602082015260035460408201526004546060820152600554608082015260065460a082015260075460c082015260085460e0820152600954610100820152600a54610120820152600b54610140820152600c54610160820152600d54610180820152600e546101a0820152600f546101c08201526010546101e0820152601154610200820152601254610220820152601354610240820152601454828201526040519283526020810151602084015260408101516040840152606081015160608401526080810151608084015260a081015160a084015260c081015160c084015260e081015160e08401526101008101516101008401526101208101516101208401526101408101516101408401526101608101516101608401526101808101516101808401526101a08101516101a08401526101c08101516101c08401526101e08101516101e08401526102008101516102008401526102208101516102208401526102408101516102408401520151610260820152f35b503461038a57602036600319011261038a576108676132bd565b6020546001600160a01b031633036104d8576001600160a01b031680156108c157602180546001600160a01b031916821790557f950a88e6770f863d57a76f0536e93cc7c318c1188c4c189aa9d7a98a6e62f8648280a280f35b63d92e233d60e01b8252600482fd5b503461038a578060031936011261038a57601c546040516001600160a01b039091168152602090f35b503461038a57604036600319011261038a576109136132bd565b5061091c6132d3565b50601f546001600160a01b0316330361060e57610937613588565b602060405160018152f35b503461038a57602036600319011261038a576019546004359060181c6001600160a01b031633036104d857610975613588565b61097d6138fc565b61098561419a565b90808210156109dd57505b80156109ce5761099f8161421b565b6040519081527fac59582e5396aca512fa873a2047e7f4c80f8f55d4a06cb34a78a0187f62719f60203392a280f35b632ca2f52b60e11b8252600482fd5b9050610990565b503461038a578060031936011261038a57601b546040516001600160a01b039091168152602090f35b503461038a57602036600319011261038a5760195460181c6001600160a01b0316338190036104d857610a3e613588565b610a466138fc565b610a56610a51613dca565b613e8a565b9394610a65838795949561344b565b95610a7984610a74858461342a565b61342a565b968715610c84578760043510610c7557426058556059879055601f546040516318160ddd60e01b81528a946001600160a01b03909216939291602082600481885afa938415610c6a5788948c938891610c20575b5091610b0b859492610af1610ae9610a7496610b159a996139f0565b605a5461342a565b605a55610b0086600b5461342a565b600b5560075461344b565b6007553090613c64565b90803b15610c07576040516326535a8160e01b8152600481019290925230602483015282908290604490829084905af1801561049657610c0b575b50601c54605d546001600160a01b039182169116813b15610c0757829160648392604051948593849263acc10f1160e01b845260048401528a60248401528860448401525af1801561049657610bee575b5050604080519586526020860192909252908401919091526060830152608082015233907f62822f533950622f8ee70b321a9d4bced56e38f52fd791b8be97e09086cd68af9060a090a280f35b81610bf89161320a565b610c0357855f610ba1565b8580fd5b8280fd5b81610c159161320a565b610c0357855f610b50565b97505093509190506020853d602011610c62575b81610c416020938361320a565b81010312610c5e5793518a94879390928b92909190610b0b610acd565b5f80fd5b3d9150610c34565b6040513d88823e3d90fd5b632ca2f52b60e11b8952600489fd5b63589b68c960e01b8952600489fd5b503461038a578060031936011261038a57601d546040516001600160a01b039091168152602090f35b503461038a57602036600319011261038a57610cd66132bd565b601f546001600160a01b031633036104d8576020546001600160a01b0391821691168114159081610d0a575b5061060e5780f35b60195460181c6001600160a01b0316141590505f610d02565b503461038a57604036600319011261038a576004356001600160401b03811161049257610d549036906004016133fa565b6024929192356001600160401b038111610c0757610d769036906004016133fa565b6020546001600160a01b03163303610e6657601f546001600160a01b031694853b15610e62579260405193630eaec18160e21b855281604486016040600488015252606485019091865b818110610e3657505084810360031901602486015282815260200192919050845b818110610e1557505050818394818581819503925af1801561049657610e045750f35b81610e0e9161320a565b61038a5780f35b909192602080600192610e278761335a565b15158152019401929101610de1565b909160019060209081906001600160a01b03610e51886132e9565b168152019401910192919092610dc0565b8480fd5b6282b42960e81b8452600484fd5b503461038a578060031936011261038a57602060ff60195460081c166040519015158152f35b503461038a578060031936011261038a57601854804210908115610fa7575b50610f9857601a805460ff60a01b1916600360a11b179055600654601f546040516278744560e21b815290602090829060049082906001600160a01b03165afa908115610f8d578391610f3b575b507ff5c2e2ff5a0aa88c6e9352be55b58e2c44042366ea1a2e7a63b3b8ad3ef36c499160409182519182526020820152a180f35b90506020813d602011610f85575b81610f566020938361320a565b81010312610c5e57517ff5c2e2ff5a0aa88c6e9352be55b58e2c44042366ea1a2e7a63b3b8ad3ef36c49610f07565b3d9150610f49565b6040513d85823e3d90fd5b63621e25c360e01b8152600490fd5b9050155f610eb9565b503461038a57606036600319011261038a57604051610fce816131a3565b6004358152602081019060243582526040810190604435825260018060a01b03601e54163303610e66575191826015555190816016555191826017557f4cf2ac0ad075ffb506f42eb2a06e69cc4204d34ae016ec7fb8175deb07fc312d8480a480f35b503461038a578060031936011261038a575061107a60405161105460408261320a565b60058152640352e302e360dc1b60208201526040519182916020835260208301906133d6565b0390f35b503461038a578060031936011261038a5760206040516a0c097ce7bc90715b34b9f160241b8152f35b503461038a578060031936011261038a57601d546001600160a01b03168015611135576020546110e2916001600160a01b0390911690613ffe565b6110ea6138fc565b620151804201804211611121576018557f213beaf8edb2ee3432f9a9bf220091337cc7ad2255d055b87295c358927732758180a180f35b634e487b7160e01b82526011600452602482fd5b506020546001600160a01b031633146110e2576282b42960e81b8152600490fd5b503461038a578060031936011261038a57601a546001600160a01b03811633036111e757601980546301000000600160b81b0319811633601881811b6301000000600160b81b0316929092179093556001600160a01b0319909316601a5590911c6001600160a01b03167f7d35bbbf65726702b94fcdce7962d3ed40813b4e323b3dce330b4f39a9b5678e8380a380f35b637b96997560e01b8252600482fd5b503461038a578060031936011261038a576020546001600160a01b0316330361060e57806018557f2446e665433d1b4519764cc05a1376b8b2356182dafd4c83b63b414d6d0877a98180a180f35b503461038a578060031936011261038a576021546040516001600160a01b039091168152602090f35b503461038a57602036600319011261038a576112876132bd565b50602061129261370c565b604051908152f35b503461038a576112a9366132fd565b5050601f546001600160a01b03163303915061060e9050576040906112cc613588565b6112d461370c565b82519182526020820152f35b503461038a576112ef3661322b565b6020546001600160a01b031633036104d85760ff601a5460a01c1660088110156104b057611532576101008101805160e083019081519081156115235781811061152357066115145760195460181c6001600160a01b03161561150557839060c084015180155f146114ff57506202a300925b610140850151806114f957506202a300945b6101208101519461016082015160a083019788519786519860808601998a519187519260208901519b60408a019c8d519a6060019a8b51918d51978c6040516113bc816131d2565b8281528360208201528460408201528560608201528660808201528660a08201528660c08201528660e08201528761010082015288610120820152866101408201528961016082015286610180820152866101a0820152866101c08201528a6101e08201528b6102008201528c6102208201528d6102408201526102600152600155600255600355600455806005558060065580600755600855600955600a558c600b55600c558b600d558b600e558b600f5560105560115560125560135560145561148661406b565b601a805460ff60a01b1916600160a01b17905542600e555192519351945191519051604080519485526020850195909552938301949094526060820152608081019290925260a08201527f1908367aaa484e6384b9bfcacded491c394fcc26a1e4371cad1497b2ab32f0d29060c090a180f35b94611374565b92611362565b63d92e233d60e01b8452600484fd5b6344d080eb60e11b8452600484fd5b6328df285560e01b8652600486fd5b63166b30ff60e11b8252600482fd5b503461038a57604036600319011261038a5761155b6132bd565b506115646132d3565b5060206040515f198152f35b503461038a578060031936011261038a576040805161158e816131a3565b828152826020820152015260606040516115a7816131a3565b601554908181526016546020820190815260406017549201918252604051928352516020830152516040820152f35b503461038a57602036600319011261038a576115f06132bd565b6020546001600160a01b031633036104d8576001600160a01b031680156108c157601e80546001600160a01b031916821790557fbb75484da0d5291c0ccc8e65abd3d469e055aa189f31c7bd824ada5936ebfbbd8280a280f35b503461038a57602036600319011261038a5761166461334b565b6020546001600160a01b031633036104d85760207f641a92ef68a52d8da528c56ccfda46f5e141262f154a6a8c075306fdea6d2b5291151560195462ff00008260101b169062ff0000191617601955604051908152a180f35b503461038a578060031936011261038a57602060ff601954166040519015158152f35b503461038a57602036600319011261038a57600435906001600160401b03821161038a57610100600319833603011261038a5760405161010081018181106001600160401b03821117611c465760405261173c836004016132e9565b815261174a602484016132e9565b906020810191825261175e604485016132e9565b60408201908152611771606486016132e9565b916060810192835260848601356003811015610c03576080820190815261179a60a4880161335a565b60a0830190815260c48801356001600160401b038111611c42576117c490600436918b01016133b8565b9760c0840198895260e48101356001600160401b038111611c3e576117ee913691016004016133b8565b9460e084019586525f5160206145395f395f51905f52549660ff8860401c1615976001600160401b03811680159081611c36575b6001149081611c2c575b159081611c23575b50611c145767ffffffffffffffff1981166001175f5160206145395f395f51905f525588611be8575b50611866614440565b61186e614440565b60ff195f5160206145195f395f51905f5254165f5160206145195f395f51905f5255611898614440565b84516001600160a01b031615611bd95780516001600160a01b031615611bd95785516001600160a01b031615611bd95781516001600160a01b031615611bd9578451605d80546001600160a01b03199081166001600160a01b039384169081179092559251601b805485169184169190911790558751601c80548516918416919091179055601a805460ff60a01b1916905592516020805490931691161790556019805460ff191660011790559051915191151591906003821015611bc5578798979697519551926004602060018060a01b03601b54166040519283809263d8a06f7360e01b82525afa928315611bba576020958a928395611b97575b506119e9906119d76040519b8c9889978896639fd29a9360e01b885260048801523060248801526044870152606486015260e0608486015260e48501906133d6565b8381036003190160a4850152906133d6565b6a0c097ce7bc90715b34b9f160241b60c483015203926001600160a01b03165af1928315611b5f5784936020918591611b6a575b50601f80546001600160a01b0319166001600160a01b03928316908117909155835160405163095ea7b360e01b815260048101929092525f19602483015290958692604492849291165af1908115611b5f57602093604492611b44575b5051915160405163095ea7b360e01b81526001600160a01b0391821660048201525f1960248201529485938492165af18015610f8d57611b15575b50611abd5780f35b68ff0000000000000000195f5160206145395f395f51905f5254165f5160206145395f395f51905f52557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a180f35b611b369060203d602011611b3d575b611b2e818361320a565b810190613570565b505f611ab5565b503d611b24565b611b5a90853d8711611b3d57611b2e818361320a565b611a7a565b6040513d86823e3d90fd5b611b8a9150823d8411611b90575b611b82818361320a565b8101906134c3565b5f611a1d565b503d611b78565b6119e9919550611bb390883d8a11611b9057611b82818361320a565b9490611995565b6040513d8b823e3d90fd5b634e487b7160e01b88526021600452602488fd5b63d92e233d60e01b8952600489fd5b68ffffffffffffffffff191668010000000000000001175f5160206145395f395f51905f52555f61185d565b63f92ee8a960e01b8a5260048afd5b9050155f611834565b303b15915061182c565b8a9150611822565b8880fd5b8780fd5b634e487b7160e01b83526041600452602483fd5b503461038a578060031936011261038a5760195460081c60ff1615611db057601b5460405163be7c13f760e01b815290602090829060049082906001600160a01b03165afa801561049657611cc3918391611d91575b506020546001600160a01b031690613ffe565b601b5460405163be7c13f760e01b815290602090829060049082906001600160a01b03165afa908115610496578291611d72575b506001600160a01b03163314611d62575b611d10613588565b600160ff195f5160206145195f395f51905f525416175f5160206145195f395f51905f52557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a180f35b805460ff19166001178155611d08565b611d8b915060203d602011611b9057611b82818361320a565b5f611cf7565b611daa915060203d602011611b9057611b82818361320a565b5f611cb0565b6020546001600160a01b03163314611cc3576282b42960e81b8152600490fd5b503461038a57602036600319011261038a57611dea6132bd565b506020600b54604051908152f35b503461038a578060031936011261038a5760195460405160189190911c6001600160a01b03168152602090f35b503461038a57604036600319011261038a57611e3f6132bd565b50611e486132d3565b50602061129261417f565b503461038a578060031936011261038a5760195460181c6001600160a01b0316330361060e57611e81613588565b611e896135af565b611e9161402c565b611ea06006546008549061342a565b60045411611f0357611eb061406b565b611eba6002613458565b42600d55611ec661419a565b80611ef4575b507fa222645ee08a6672a8a0b45153fe630c9bb71dbd07af71b39941e29f8bd428768180a180f35b611efd9061421b565b5f611ecc565b634075283f60e01b8152600490fd5b503461038a578060031936011261038a57601a546040516001600160a01b039091168152602090f35b503461038a57602036600319011261038a57611f556132bd565b602054906001600160a01b0382163303611fbe576001600160a01b0316908115611faf576001600160a01b03191681176020557f16bd984efd11b8bbf2957266a55bcb3b3721d05b70044d559f0c58d80f8bd36c8280a280f35b63d92e233d60e01b8352600483fd5b6282b42960e81b8352600483fd5b503461038a578060031936011261038a57611fe5613588565b601c546001600160a01b0316330361060e57600e546120015780f35b61060b61406b565b503461038a578060031936011261038a576020611292613513565b503461038a57612033366132fd565b50601f549193909250906001600160a01b0316330361060e575061207e6120869261205c613588565b6120646136cc565b61206d816138c5565b612076816138de565b60085461342a565b6008556135d9565b602061129261370c565b503461038a578060031936011261038a57602060ff5f5160206145195f395f51905f5254166040519015158152f35b503461038a578060031936011261038a57601e546040516001600160a01b039091168152602090f35b503461038a578060031936011261038a57602060405160018152f35b503461038a57602036600319011261038a5761211e6132bd565b6020546001600160a01b031633036104d85761213861402c565b6001600160a01b031680156108c157602280546001600160a01b031916821790557f6536690106168bdf4ba72c128a053d817999b1db90cae23f139b293bf862cb758280a280f35b503461038a578060031936011261038a577f000000000000000000000000d0a53e724ea9cb041e30f0243e3c84bdea238dfa6001600160a01b031630036121d85760206040515f5160206144f95f395f51905f528152f35b63703e46dd60e11b8152600490fd5b50604036600319011261038a576121fc6132bd565b906024356001600160401b03811161049257366023820112156104925761222d903690602481600401359101613382565b6001600160a01b037f000000000000000000000000d0a53e724ea9cb041e30f0243e3c84bdea238dfa16308114908115612430575b506124215760195460081c60ff161561240157601b5460405163be7c13f760e01b815290602090829060049082906001600160a01b03165afa908115610f8d57906122c0918491611d9157506020546001600160a01b031690613ffe565b6040516352d1902d60e01b8152926001600160a01b0381169190602085600481865afa809585966123cd575b5061230557634c9c8ce360e01b84526004839052602484fd5b9091845f5160206144f95f395f51905f5281036123bb5750813b156123a9575f5160206144f95f395f51905f5280546001600160a01b031916821790557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8480a2815183901561238f578083602061238b95519101845af461238561446b565b9161449a565b5080f35b5050503461239a5780f35b63b398979f60e01b8152600490fd5b634c9c8ce360e01b8452600452602483fd5b632a87526960e21b8552600452602484fd5b9095506020813d6020116123f9575b816123e96020938361320a565b81010312610e625751945f6122ec565b3d91506123dc565b6020546001600160a01b031633146122c0576282b42960e81b8252600482fd5b63703e46dd60e11b8252600482fd5b5f5160206144f95f395f51905f52546001600160a01b0316141590505f612262565b503461038a578060031936011261038a5761246b613588565b61247361402c565b61248e612485600e546011549061342a565b6012549061342a565b421115611f0357601a805460ff60a01b1916600160a21b1790557fb9d6f6ff8a5599d0d4b1aff8493383c1a00535e647c270af1ecb4649934b4f228180a180f35b503461038a578060031936011261038a57602080546040516001600160a01b039091168152f35b503461038a57602036600319011261038a576125106132bd565b6020546001600160a01b031633036104d8576001600160a01b031680156108c157601d80546001600160a01b031916821790557f5ef7f35db90768a7ec0edc8fcf3bdccbd2b68543ec71389db2d555ff07b282a58280a280f35b503461038a578060031936011261038a5760195460081c60ff16156126b557601b5460405163be7c13f760e01b815290602090829060049082906001600160a01b03165afa8015610496576125d2918391611d9157506020546001600160a01b031690613ffe565b805460ff81168061264f575b6104d85760ff191681555f5160206145195f395f51905f525460ff8116156126405760ff19165f5160206145195f395f51905f52557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a180f35b638dfc202b60e01b8252600482fd5b50601b5460405163be7c13f760e01b815290602090829060049082906001600160a01b03165afa908115610f8d578391612696575b506001600160a01b03163314156125de565b6126af915060203d602011611b9057611b82818361320a565b5f612684565b6020546001600160a01b031633146125d2576282b42960e81b8152600490fd5b503461038a578060031936011261038a576020604051620151808152f35b503461038a578060031936011261038a5761107a61270f613494565b604080519586526020860194909452928401919091526060830152608082015290819060a0820190565b503461038a578060031936011261038a57605d546040516001600160a01b039091168152602090f35b503461038a57602036600319011261038a5760195460181c6001600160a01b0316330361060e57612791613588565b6127a0600d546010549061342a565b42108061289f575b612890576127b46138fc565b600754612881576006805490829055601a805460ff60a01b1916600560a01b1790556019546127f2908290309060181c6001600160a01b0316613c64565b601f5482906001600160a01b0316803b15610492576040516326535a8160e01b8152600481018490523060248201529082908290818381604481015b03925af180156104965761286c575b507f9a7851747cd7ffb3fe0a32caf3da48b31f27cebe131267051640f8b72fc47186602083604051908152a180f35b816128769161320a565b61049257815f61283d565b631ee1e36d60e21b8152600490fd5b63f37d71eb60e01b8152600490fd5b5060ff601a5460a01c1660088110156128bb57600514156127a8565b634e487b7160e01b82526021600452602482fd5b503461038a578060031936011261038a5760206040516301e133808152f35b503461038a578060031936011261038a57612907613588565b61290f6138fc565b602061291a33613d0b565b6040518181527ff9984c8173c4b4ff9cc454f76ed19c053f4490ccb224fd9ddffeb4ef4bea3530833392a2604051908152f35b503461038a578060031936011261038a5760195460181c6001600160a01b0316330361060e5761297b613588565b6129836138fc565b612995600d54600c546057549161393c565b80612aaf575b50600654601c54604051635f72b8c160e01b81523060048201528391602090829060249082906001600160a01b03165afa908115610496578291612a7a575b508015612a70576129ee612a16918461396d565b600683905560078390556008839055601a805460ff60a01b1916600560a01b1790558361342a565b601954612a32908290309060181c6001600160a01b0316613c64565b601f546001600160a01b0316803b15610c07576040516326535a8160e01b81526004810192909252306024830152829082908183816044810161282e565b50612a16816129ee565b9150506020813d602011612aa7575b81612a966020938361320a565b81010312610c5e578290515f6129da565b3d9150612a89565b605954905f198101818111612b00578210612af1576001149081612ae8575b50612ad9575f61299b565b635f73a3b760e01b8152600490fd5b9050155f612ace565b635f73a3b760e01b8352600483fd5b634e487b7160e01b84526011600452602484fd5b503461038a57602036600319011261038a57602054600435906001600160a01b031633036104d857620f42408111612b78576020817f9530355cac0969404662db60e90664d90fdf47c37c69fa53616fc509fde4c29492600555604051908152a180f35b63e56d58cf60e01b8252600482fd5b503461038a57602036600319011261038a57612ba161334b565b6020546001600160a01b031633036104d85760207f692eab81334fcd354cab995569c5a935a76a8abeca88863819ab4e5c78e1d16191151560195461ff008260081b169061ff00191617601955604051908152a180f35b503461038a578060031936011261038a576022546040516001600160a01b039091168152602090f35b503461038a578060031936011261038a5760ff601a5460a01c166040519060088110156104b057602092508152f35b503461038a57602036600319011261038a57612c6a61334b565b6020546001600160a01b031633036104d857601f5482916001600160a01b0390911690813b15612cc25782916024839260405194859384926324a16c6160e01b8452151560048401525af1801561049657610e045750f35b5050fd5b503461038a57602036600319011261038a57612ce061334b565b6020546001600160a01b031633036104d85760207f2cbdf9efae4202902b928a7521f81e3e5253fecaf3c7a66f2bd8b337310e32b891151560ff196019541660ff821617601955604051908152a180f35b503461038a57602036600319011261038a57612d4b6132bd565b6020546001600160a01b031633036104d8576001600160a01b031680156108c157601a80546001600160a01b031916821790557f10f06072822ef73860fedb88933f968d20bb4aadce8a8d360d1124cb6ce1e0b28280a280f35b503461038a57612db4366132fd565b50601f54909392506001600160a01b031633036104d857612dd3613588565b612ddb6136cc565b612de361370c565b916a0c097ce7bc90715b34b9f160241b83612dff828286613bdf565b9309612e1b575b506020926112929161207e9061206d816138c5565b60018201809211612e2e57506020612e06565b634e487b7160e01b81526011600452602490fd5b503461038a57608036600319011261038a57612e5c6132bd565b50612e656132d3565b604435906001600160a01b0382168203610c0757601f546001600160a01b03163303611fbe5790612e9b61060b92612e9b613588565b6135d9565b503461038a578060031936011261038a57602060ff60195460101c166040519015158152f35b5034610c5e576020366003190112610c5e57612ee06132bd565b6020546001600160a01b03163303612f5857601f546001600160a01b031690813b15610c5e57604051636c19e78360e01b81526001600160a01b039091166004820152905f908290602490829084905af18015612f4d57612f3f575080f35b612f4b91505f9061320a565b005b6040513d5f823e3d90fd5b6282b42960e81b5f5260045ffd5b34610c5e57612f743661322b565b6020546001600160a01b03163303612f5857612f8e6135af565b6040810190815160035411613055576101408101805160125410613046576130417f1908367aaa484e6384b9bfcacded491c394fcc26a1e4371cad1497b2ab32f0d29351928360035560608101519283600455602082015160025581516001556101208201516011555160125542600f5560095490600a5461010060e083015192015192604051968796879260a094919796959260c0850198855260208501526040840152606083015260808201520152565b0390a1005b63a87681b160e01b5f5260045ffd5b6319cf95fb60e11b5f5260045ffd5b34610c5e575f366003190112610c5e5760195460181c6001600160a01b03163303612f5857613091613588565b6130996135af565b6130a161402c565b6130b06006546008549061342a565b60045411613111576130c061406b565b6130ca6003613458565b42600d556130d661419a565b80613102575b7fa222645ee08a6672a8a0b45153fe630c9bb71dbd07af71b39941e29f8bd428765f80a1005b61310b9061421b565b806130dc565b634075283f60e01b5f5260045ffd5b34610c5e576020366003190112610c5e576004359063ffffffff60e01b8216809203610c5e576020916351f695a360e11b8114908115613192575b8115613181575b8115613170575b5015158152f35b6301ffc9a760e01b14905083613169565b6332b6874d60e01b81149150613162565b63a04fd5db60e01b8114915061315b565b606081019081106001600160401b038211176131be57604052565b634e487b7160e01b5f52604160045260245ffd5b61028081019081106001600160401b038211176131be57604052565b61016081019081106001600160401b038211176131be57604052565b90601f801991011681019081106001600160401b038211176131be57604052565b610180906003190112610c5e5760405161018081018181106001600160401b038211176131be576040526004358152602435602082015260443560408201526064356060820152608435608082015260a43560a082015260c43560c082015260e43560e08201526101043561010082015261012435610120820152610144356101408201526101643561016082015290565b600435906001600160a01b0382168203610c5e57565b602435906001600160a01b0382168203610c5e57565b35906001600160a01b0382168203610c5e57565b6080906003190112610c5e576004356001600160a01b0381168103610c5e5790602435906044356001600160a01b0381168103610c5e57906064356001600160a01b0381168103610c5e5790565b600435908115158203610c5e57565b35908115158203610c5e57565b6001600160401b0381116131be57601f01601f191660200190565b92919261338e82613367565b9161339c604051938461320a565b829481845281830111610c5e578281602093845f960137010152565b9080601f83011215610c5e578160206133d393359101613382565b90565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9181601f84011215610c5e578235916001600160401b038311610c5e576020808501948460051b010111610c5e57565b9190820180921161343757565b634e487b7160e01b5f52601160045260245ffd5b9190820391821161343757565b600881101561348057601a805460ff60a01b191660a09290921b60ff60a01b16919091179055565b634e487b7160e01b5f52602160045260245ffd5b61349c613dca565b6020810151156134b6576134af90613e8a565b9091929394565b505f905f905f905f905f90565b90816020910312610c5e57516001600160a01b0381168103610c5e5790565b8181029291811591840414171561343757565b81156134ff570490565b634e487b7160e01b5f52601260045260245ffd5b601454605954600181018091116134375761353e90613538600d5491600c54906134e2565b9061342a565b90613549818361342a565b42116135555750505f90565b61356661356c91620151809361342a565b4261344b565b0490565b90816020910312610c5e57518015158103610c5e5790565b60ff5f5160206145195f395f51905f5254166135a057565b63d93c066560e01b5f5260045ffd5b60ff601a5460a01c166008811015613480576001036135ca57565b630d13988560e11b5f5260045ffd5b601f546040516370a0823160e01b81526001600160a01b03928316600482018190529092909160209184916024918391165afa918215612f4d575f92613698575b50605a5491801561368957815f52605b60205261363b60405f20548461344b565b908115613683576a0c097ce7bc90715b34b9f160241b9161365b916134e2565b04815f52605c60205261367360405f2091825461342a565b90555f52605b60205260405f2055565b50505050565b505f52605b60205260405f2055565b9091506020813d6020116136c4575b816136b46020938361320a565b81010312610c5e5751905f61361a565b3d91506136a7565b60ff601a5460a01c1660088110156134805760018114159081613700575b506136f157565b6301ec871560e51b5f5260045ffd5b6003915014155f6136ea565b60ff601a5460a01c16613724600e546011549061342a565b421115806138a5575b8015613892575b61387f5760088110156134805760058114908115613874575b5061380e5760565460575461376881600d54600c549061393c565b906001820180921161343757808210156137e457826a0c097ce7bc90715b34b9f160241b0192836a0c097ce7bc90715b34b9f160241b11613437576137b06137b6938361344b565b90613bdf565b6a0c097ce7bc90715b34b9f160241b01806a0c097ce7bc90715b34b9f160241b11613437576133d3916139f0565b50506a0c097ce7bc90715b34b9f160241b01806a0c097ce7bc90715b34b9f160241b116134375790565b601f54604051633c46796f60e11b815290602090829060049082906001600160a01b03165afa908115612f4d575f91613845575090565b90506020813d60201161386c575b816138606020938361320a565b81010312610c5e575190565b3d9150613853565b60079150145f61374d565b506a0c097ce7bc90715b34b9f160241b90565b5060088110156134805760048114613734565b506008811015613480576002811415801561372d5750600381141561372d565b600154116138cf57565b632ca2f52b60e11b5f5260045ffd5b6138e661417f565b106138ed57565b63e4bac01b60e01b5f5260045ffd5b60ff601a5460a01c1660088110156134805760028114159081613930575b5061392157565b639adffb1560e01b5f5260045ffd5b6003915014155f61391a565b90811561396657613950613955924261344b565b6134f5565b81811115613961575090565b905090565b5050505f90565b9190915f838202915f19858209918380841093039280840393146139e25782620f424011156139d357507fde8f6cefed634549b62c77574f722e1ac57e23f24d8fd5cb790fb65668c261399394620f4240910990828211900360fa1b910360061c170290565b63227bc15360e01b8152600490fd5b505050620f42409192500490565b906a0c097ce7bc90715b34b9f160241b8202905f196a0c097ce7bc90715b34b9f160241b840992828085109403938085039414613aa15783821115613a92576a0c097ce7bc90715b34b9f160241b829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b63227bc15360e01b5f5260045ffd5b50906133d392506134f5565b9190915f838202915f1985820991838084109303928084039314613b2957826a0c097ce7bc90715b34b9f160241b11156139d357507f356317ffa45f828d2a41175b2aed3a20e7fdd4a4e46cc11944d93b01be7b171193946a0c097ce7bc90715b34b9f160241b910990828211900360dc1b910360241c170290565b5050506a0c097ce7bc90715b34b9f160241b9192500490565b905f6a0c097ce7bc90715b34b9f160241b8302905f196a0c097ce7bc90715b34b9f160241b850990828083109203918083039214613bd257816301e1338011156139d357506301e133806a0c097ce7bc90715b34b9f160241b7f98f5be4dd1e14769fbd6666224dc1eb80dd2e0a3d2c8b328f57e76b7ae10395794950990828211900360f91b910360071c170290565b50506301e1338090049150565b9091828202915f1984820993838086109503948086039514613c575784831115613a9257829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b5050906133d392506134f5565b605d546040516323b872dd60e01b602082019081526001600160a01b03938416602483015293831660448201526064808201959095529384521691613cc8915f91829190613cb360848261320a565b519082865af1613cc161446b565b908361449a565b8051908115159182613cf0575b5050613cde5750565b635274afe760e01b5f5260045260245ffd5b613d039250602080918301019101613570565b155f80613cd5565b90613d15826135d9565b6001600160a01b0382165f818152605c602052604090205492908315613dc3575f52605c6020525f6040812055600b54838082115f14613dbb57613d589161344b565b600b55601f546001600160a01b0316803b15610c5e5760405163fb53db6d60e01b8152600481018590526001600160a01b0390921660248301525f908290818381604481015b03925af18015612f4d57613daf5750565b5f613db99161320a565b565b50505f613d58565b505f925050565b5f610140604051613dda816131ee565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e082015282610100820152826101208201520152601054600d54600c5460065460145490600a549260575460555490605854926059549460ff60195416986040519a613e4e8c6131ee565b8b5260208b015260408a01526060890152608088015260a087015260c086015260e0850152610100840152610120830152151561014082015290565b9060c08201519160208101805191613eb760e08201519560608301968751906080850196875193426142b6565b15613fdc57600754915b601c54604051631d5f24ef60e11b815230600482015293976001600160a01b03909116958894936020856024818b5afa948515612f4d575f95613fa7575b506020613f0e6024968861396d565b98604051968780926350355cb960e11b82523060048301525afa948515612f4d575f95613f6d575b5090613f55613f619392613f6a96604087015191845191885193614329565b9651915190519161393c565b8093429061439e565b93565b945091906020853d602011613f9f575b81613f8a6020938361320a565b81010312610c5e579351939091613f55613f36565b3d9150613f7d565b94506020853d602011613fd4575b81613fc26020938361320a565b81010312610c5e579351936020613eff565b3d9150613fb5565b613ff86040830151613ff28360a08601516134e2565b90613aad565b91613ec1565b6001600160a01b03163314159081614018575b50612f5857565b6001600160a01b031633141590505f614011565b60ff601a5460a01c1660088110156134805760028114908115614060575b5061405157565b63ac9d28cd60e01b5f5260045ffd5b60039150145f61404a565b601054600c54600954601c54604051631d5f24ef60e11b81523060048201529293919290602090829060249082906001600160a01b03165afa908115612f4d575f9161414d575b50620f424003620f42408111613437576140cc908361396d565b906141036140d982613b42565b946140fd6140e682613b42565b966140f7605954986058549861396d565b9561396d565b926134f5565b9160405160a081018181106001600160401b038211176131be5786916080916040528381528460208201528560408201528660608201520152605555605655605755605855605955565b90506020813d602011614177575b816141686020938361320a565b81010312610c5e57515f6140b2565b3d915061415b565b6133d36141916003546006549061344b565b6008549061344b565b600854601f54604051634f98428960e11b815290602090829060049082906001600160a01b03165afa908115612f4d575f916141e9575b50808211156141e3576133d39161344b565b50505f90565b90506020813d602011614213575b816142046020938361320a565b81010312610c5e57515f6141d1565b3d91506141f7565b6142278160085461344b565b6008556142368160065461342a565b60065561427561426d614267600d54614262600c5461425c60575480926055549561393c565b9061344b565b6134e2565b83613aad565b60075461342a565b600755601f546001600160a01b0316803b15610c5e5760405163fb53db6d60e01b815260048101929092523360248301525f90829081838160448101613d9e565b93909182156142ff5750818411156142f4576139506142d8926142de9561344b565b9261344b565b905b8181106142ee575060019091565b915f9150565b50505050505f905f90565b9384919350809250111561431f5761431a926139509161344b565b6142e0565b505050505f905f90565b929381158015614396575b61438d5761426261426292614348956134e2565b9080651cae8c13e0000290651cae8c13e0008204036134375761436d81600184613bdf565b9181156134ff576001900961437f5790565b600181018091116134375790565b50505050505f90565b508215614334565b919290610140820151156144385760e082015180941115614438575f93602083015160018201809211613437576135386143dd926060860151906134e2565b61010083016143ed81518361342a565b8311614417575b505083159050613966576133d392610120614411920151906134e2565b9061396d565b6201518093955061442e929161425c91519061342a565b04915f80806143f4565b505050505f90565b60ff5f5160206145395f395f51905f525460401c161561445c57565b631afcd79f60e31b5f5260045ffd5b3d15614495573d9061447c82613367565b9161448a604051938461320a565b82523d5f602084013e565b606090565b906144be57508051156144af57805190602001fd5b630a12f52160e11b5f5260045ffd5b815115806144ef575b6144cf575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156144c756fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbccd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220e48cdb9ac2ac6c188aaeef1e0f88b4bbdc0221247fbae542096a1b53fab78a1964736f6c634300081b0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in MON
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
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.