Source Code
More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 3,923 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Liquidate Loan | 24348710 | 17 days ago | IN | 0 ETH | 0.00053109 | ||||
| Liquidate Loan | 24348706 | 17 days ago | IN | 0 ETH | 0.00052949 | ||||
| Liquidate Loan | 24348703 | 17 days ago | IN | 0 ETH | 0.00057687 | ||||
| Liquidate Loan | 24348699 | 17 days ago | IN | 0 ETH | 0.00055814 | ||||
| Liquidate Loan | 24301312 | 24 days ago | IN | 0 ETH | 0.00040351 | ||||
| Liquidate Loan | 24234291 | 33 days ago | IN | 0 ETH | 0.00007919 | ||||
| Liquidate Loan | 24162175 | 43 days ago | IN | 0 ETH | 0.00005073 | ||||
| Liquidate Loan | 24162172 | 43 days ago | IN | 0 ETH | 0.00021706 | ||||
| Liquidate Loan | 24162170 | 43 days ago | IN | 0 ETH | 0.00021725 | ||||
| Liquidate Loan | 24162167 | 43 days ago | IN | 0 ETH | 0.00010458 | ||||
| Liquidate Loan | 24162163 | 43 days ago | IN | 0 ETH | 0.000065 | ||||
| Liquidate Loan | 24162161 | 43 days ago | IN | 0 ETH | 0.00005936 | ||||
| Liquidate Loan | 24162159 | 43 days ago | IN | 0 ETH | 0.00005886 | ||||
| Liquidate Loan | 24162143 | 43 days ago | IN | 0 ETH | 0.00005885 | ||||
| Liquidate Loan | 24162140 | 43 days ago | IN | 0 ETH | 0.0000593 | ||||
| Liquidate Loan | 24162137 | 43 days ago | IN | 0 ETH | 0.00021775 | ||||
| Liquidate Loan | 24162112 | 43 days ago | IN | 0 ETH | 0.00005941 | ||||
| Liquidate Loan | 24162109 | 43 days ago | IN | 0 ETH | 0.00005813 | ||||
| Liquidate Loan | 24162103 | 43 days ago | IN | 0 ETH | 0.00008042 | ||||
| Liquidate Loan | 24162100 | 43 days ago | IN | 0 ETH | 0.00005416 | ||||
| Liquidate Loan | 24162095 | 43 days ago | IN | 0 ETH | 0.00010254 | ||||
| Liquidate Loan | 24162085 | 43 days ago | IN | 0 ETH | 0.00007163 | ||||
| Liquidate Loan | 24162081 | 43 days ago | IN | 0 ETH | 0.00005903 | ||||
| Liquidate Loan | 24162078 | 43 days ago | IN | 0 ETH | 0.00011232 | ||||
| Liquidate Loan | 24162074 | 43 days ago | IN | 0 ETH | 0.00021241 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
MultiSourceLoan
Compiler Version
v0.8.21+commit.d9974bed
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
import "@delegate/IDelegateRegistry.sol";
import "@solmate/tokens/ERC20.sol";
import "@solmate/tokens/ERC721.sol";
import "@solmate/utils/FixedPointMathLib.sol";
import "@solmate/utils/ReentrancyGuard.sol";
import "@solmate/utils/SafeTransferLib.sol";
import "../../interfaces/INFTFlashAction.sol";
import "../../interfaces/loans/IMultiSourceLoan.sol";
import "../../interfaces/ILoanLiquidator.sol";
import "../utils/Hash.sol";
import "../utils/Interest.sol";
import "../Multicall.sol";
import "./WithCallbacks.sol";
contract MultiSourceLoan is IMultiSourceLoan, Multicall, ReentrancyGuard, WithCallbacks {
using FixedPointMathLib for uint256;
using Hash for Loan;
using Hash for SignableRepaymentData;
using Hash for RenegotiationOffer;
using Interest for uint256;
using MessageHashUtils for bytes32;
using SafeTransferLib for ERC20;
/// @notice Loan Id to hash
mapping(uint256 => bytes32) private _loans;
/// @notice Maximum number of sources per loan
uint256 private _maxSources;
/// @notice Min lock period for a source
uint256 private _minLockPeriod;
/// @notice If we have N max sources, then the min principal of any given source
/// at the time of repayment needs to be Total Principal / (N * _MAX_RATIO_SOURCE_MIN_PRINCIPAL)
/// This is captured in _getMinSourcePrincipal.
uint256 private constant _MAX_RATIO_SOURCE_MIN_PRINCIPAL = 2;
/// @notice delegate registry
IDelegateRegistry private _delegateRegistry;
/// @notice Contract to execute flash actions.
INFTFlashAction private _flashActionContract;
event MaxSourcesUpdated(uint256 newMax);
event LoanEmitted(uint256 loanId, uint256 offerId, Loan loan, address lender, address borrower, uint256 fee);
event LoanRefinanced(uint256 renegotiationId, uint256 oldLoanId, uint256 newLoanId, Loan loan, uint256 fee);
event LoanRepaid(uint256 loanId, uint256 totalRepayment, uint256 fee);
event DelegateRegistryUpdated(address newdelegateRegistry);
event Delegated(uint256 loanId, address delegate, bool value);
event FlashActionContractUpdated(address newFlashActionContract);
event FlashActionExecuted(uint256 loanId, address target, bytes data);
event RevokeDelegate(address delegate, address collection, uint256 tokenId);
event LoanExtended(uint256 oldLoanId, uint256 newLoanId, Loan loan, uint256 _extension);
event MinLockPeriodUpdated(uint256 minLockPeriod);
error InvalidMethodError();
error InvalidRenegotiationOfferError();
error TooManySourcesError(uint256 sources);
error MinLockPeriodTooHighError(uint256 minLockPeriod);
error PartialOfferCannotChangeDurationError();
error PartialOfferCannotHaveFeeError();
error LoanExpiredError();
error RefinanceFullError();
error LengthMismatchError();
error TargetPrincipalTooLowError(uint256 sourcePrincipal, uint256 loanPrincipal);
error NFTNotReturnedError();
error ExtensionNotAvailableError();
error SourceCannotBeRefinancedError(uint256 minTimestamp);
/// @param loanLiquidator Address of the liquidator contract.
/// @param protocolFee Protocol fee charged on gains.
/// @param currencyManager Address of the currency manager.
/// @param collectionManager Address of the collection manager.
/// @param maxSources Maximum number of sources per loan.
/// @param delegateRegistry Address of the delegate registry (Delegate.xyz).
/// @param flashActionContract Address of the flash action contract.
constructor(
address loanLiquidator,
ProtocolFee memory protocolFee,
address currencyManager,
address collectionManager,
uint256 maxSources,
uint256 minLockPeriod,
address delegateRegistry,
address flashActionContract
) WithCallbacks("GONDI_MULTI_SOURCE_LOAN", currencyManager, collectionManager) {
_checkAddressNotZero(loanLiquidator);
_loanLiquidator = ILoanLiquidator(loanLiquidator);
_protocolFee = protocolFee;
_maxSources = maxSources;
_minLockPeriod = minLockPeriod;
_delegateRegistry = IDelegateRegistry(delegateRegistry);
_flashActionContract = INFTFlashAction(flashActionContract);
}
/// @inheritdoc IMultiSourceLoan
function emitLoan(LoanExecutionData calldata _executionData) external nonReentrant returns (uint256, Loan memory) {
address lender = _executionData.lender;
address borrower = _executionData.borrower;
LoanOffer calldata offer = _executionData.executionData.offer;
address offerer = offer.lender == address(0) ? borrower : lender;
_validateExecutionData(
_executionData.executionData,
lender,
borrower,
offerer,
_executionData.lenderOfferSignature,
_executionData.borrowerOfferSignature
);
uint256 loanId = _getAndSetNewLoanId();
uint256 amount = _executionData.executionData.amount;
Source[] memory source = new Source[](1);
source[0] = Source(loanId, lender, amount, 0, block.timestamp, offer.aprBps);
Loan memory loan = Loan(
borrower,
_executionData.executionData.tokenId,
offer.nftCollateralAddress,
offer.principalAddress,
amount,
block.timestamp,
offer.duration,
source
);
_loans[loanId] = loan.hash();
uint256 fee = offer.fee.mulDivUp(amount, offer.principalAmount);
ProtocolFee memory protocolFee = _protocolFee;
_handleProtocolFeeForFee(
offer.principalAddress, lender, fee.mulDivUp(protocolFee.fraction, _PRECISION), protocolFee
);
ERC20(offer.principalAddress).safeTransferFrom(lender, borrower, amount - fee);
/// @dev After sending the principal to the borrower, check if there's an action to be taken (eg: use it to buy the collateral).
uint128 tax = _handleAfterPrincipalTransferCallback(loan, _executionData.executionData.callbackData, fee);
if (tax > 0) {
uint256 taxCost = amount.mulDivUp(tax, _PRECISION);
uint256 feeTax = taxCost.mulDivUp(protocolFee.fraction, _PRECISION);
ERC20(offer.principalAddress).safeTransferFrom(borrower, lender, taxCost - feeTax);
if (feeTax > 0) {
ERC20(offer.principalAddress).safeTransferFrom(borrower, protocolFee.recipient, feeTax);
}
}
ERC721(offer.nftCollateralAddress).transferFrom(borrower, address(this), _executionData.executionData.tokenId);
emit LoanEmitted(loanId, offer.offerId, loan, lender, borrower, offer.fee);
if (offer.capacity > 0) {
_used[offerer][offer.offerId] += amount;
} else {
isOfferCancelled[offerer][offer.offerId] = true;
}
return (loanId, loan);
}
/// @inheritdoc IMultiSourceLoan
function refinanceFull(
RenegotiationOffer calldata _renegotiationOffer,
Loan memory _loan,
bytes calldata _renegotiationOfferSignature
) external returns (uint256, Loan memory) {
uint256 loanId = _renegotiationOffer.loanId;
address sender = msg.sender;
bool clearsInterest = false;
_baseLoanChecks(loanId, _loan);
_baseRenegotiationChecks(_renegotiationOffer, _loan);
bool strictImprovement = msg.sender == _renegotiationOffer.lender;
(uint256 totalDelta, uint256 totalAccruedInterest, uint256 totalNewSources, uint256 totalAnnualInterest) =
_processOldSources(_renegotiationOffer, _loan, strictImprovement);
if (totalNewSources > 1) {
revert RefinanceFullError();
}
/// @dev If it's lender initiated, needs to be strictly better.
if (strictImprovement) {
_checkStrictlyBetter(
_renegotiationOffer.principalAmount,
totalDelta,
_renegotiationOffer.duration + block.timestamp,
_loan.duration + _loan.startTime,
_renegotiationOffer.aprBps,
totalAnnualInterest / _loan.principalAmount,
_renegotiationOffer.fee
);
} else if (sender != _loan.borrower) {
revert OnlyLenderOrBorrowerCallableError();
} else {
clearsInterest = true;
_checkSignature(_renegotiationOffer.lender, _renegotiationOffer.hash(), _renegotiationOfferSignature);
}
uint256 netNewLender = _renegotiationOffer.principalAmount - _renegotiationOffer.fee;
if (clearsInterest) {
netNewLender -= totalAccruedInterest;
totalAccruedInterest = 0;
}
if (totalDelta > netNewLender) {
ERC20(_loan.principalAddress).safeTransferFrom(
_loan.borrower, _renegotiationOffer.lender, totalDelta - netNewLender
);
} else if (totalDelta < netNewLender) {
ERC20(_loan.principalAddress).safeTransferFrom(
_renegotiationOffer.lender, _loan.borrower, netNewLender - totalDelta
);
}
uint256 newLoanId = _getAndSetNewLoanId();
Source[] memory newSources = new Source[](1);
newSources[0] = _getSourceFromOffer(_renegotiationOffer, totalAccruedInterest, newLoanId);
_loan.source = newSources;
_loan.duration = (block.timestamp - _loan.startTime) + _renegotiationOffer.duration;
_loan.principalAmount = _renegotiationOffer.principalAmount;
_loans[newLoanId] = _loan.hash();
delete _loans[loanId];
emit LoanRefinanced(_renegotiationOffer.renegotiationId, loanId, newLoanId, _loan, _renegotiationOffer.fee);
return (newLoanId, _loan);
}
/// @inheritdoc IMultiSourceLoan
function refinancePartial(RenegotiationOffer calldata _renegotiationOffer, Loan memory _loan)
external
returns (uint256, Loan memory)
{
uint256 loanId = _renegotiationOffer.loanId;
if (_renegotiationOffer.principalAmount < _getMinSourcePrincipal(_loan.principalAmount)) {
revert TargetPrincipalTooLowError(_renegotiationOffer.principalAmount, _loan.principalAmount);
}
if (msg.sender != _renegotiationOffer.lender) {
revert OnlyLenderCallableError();
}
_baseLoanChecks(loanId, _loan);
_baseRenegotiationChecks(_renegotiationOffer, _loan);
if (_renegotiationOffer.duration > 0) {
revert PartialOfferCannotChangeDurationError();
}
if (_renegotiationOffer.fee > 0) {
revert PartialOfferCannotHaveFeeError();
}
(uint256 totalDelta, uint256 totalAccruedInterest, uint256 totalNewSources,) =
_processOldSources(_renegotiationOffer, _loan, true);
if (totalDelta != _renegotiationOffer.principalAmount) {
revert InvalidRenegotiationOfferError();
}
if (totalNewSources > _maxSources) {
revert TooManySourcesError(totalNewSources);
}
uint256 newLoanId = _getAndSetNewLoanId();
Source[] memory newSources = new Source[](totalNewSources);
newSources[0] = _getSourceFromOffer(_renegotiationOffer, totalAccruedInterest, newLoanId);
/// @dev Index = 0 is taken by the new source
uint256 j = 1;
for (uint256 i = 0; i < _renegotiationOffer.targetPrincipal.length;) {
if (_renegotiationOffer.targetPrincipal[i] > 0) {
newSources[j] = _loan.source[i];
newSources[j].principalAmount = _renegotiationOffer.targetPrincipal[i];
unchecked {
++j;
}
}
unchecked {
++i;
}
}
_loan.source = newSources;
_loans[newLoanId] = _loan.hash();
delete _loans[loanId];
/// @dev Here fee is always 0
emit LoanRefinanced(_renegotiationOffer.renegotiationId, loanId, newLoanId, _loan, 0);
return (newLoanId, _loan);
}
/// @inheritdoc IMultiSourceLoan
function extendLoan(uint256 _loanId, Loan memory _loan, uint256 _extension)
external
returns (uint256, Loan memory)
{
_baseLoanChecks(_loanId, _loan);
if (_loan.source.length > 1) {
revert ExtensionNotAvailableError();
}
uint256 unlockedTime = _getUnlockedTime(_loan.source[0].startTime, _loan.startTime + _loan.duration);
if (unlockedTime > block.timestamp) {
revert SourceCannotBeRefinancedError(unlockedTime);
}
if (_loan.source[0].lender != msg.sender) {
revert OnlyLenderCallableError();
}
_loan.duration += _extension;
uint256 newLoanId = _getAndSetNewLoanId();
_loans[newLoanId] = _loan.hash();
delete _loans[_loanId];
emit LoanExtended(_loanId, newLoanId, _loan, _extension);
return (newLoanId, _loan);
}
/// @inheritdoc IMultiSourceLoan
function repayLoan(LoanRepaymentData calldata _repaymentData) external override nonReentrant {
uint256 loanId = _repaymentData.data.loanId;
Loan calldata loan = _repaymentData.loan;
/// @dev If the caller is not the borrower itself, check the signature to avoid someone else forcing an unwanted repayment.
if (msg.sender != loan.borrower) {
_checkSignature(loan.borrower, _repaymentData.data.hash(), _repaymentData.borrowerSignature);
}
_baseLoanChecks(loanId, loan);
/// @dev Unlikely this is used outside of the callback with a seaport sell, but leaving here in case that's not correct.
if (_repaymentData.data.shouldDelegate) {
_delegateRegistry.delegateERC721(
loan.borrower, loan.nftCollateralAddress, loan.nftCollateralTokenId, bytes32(""), true
);
}
ERC721(loan.nftCollateralAddress).transferFrom(address(this), loan.borrower, loan.nftCollateralTokenId);
/// @dev After returning the NFT to the borrower, check if there's an action to be taken (eg: sell it to cover repayment).
uint128 taxBps = _handleAfterNFTTransferCallback(loan, _repaymentData.data.callbackData);
/// @dev Bring to memory
ProtocolFee memory protocolFee = _protocolFee;
bool withProtocolFee = protocolFee.fraction > 0;
uint256 totalProtocolFee = 0;
ERC20 asset = ERC20(loan.principalAddress);
uint256 totalRepayment = 0;
for (uint256 i = 0; i < loan.source.length;) {
Source memory source = loan.source[i];
uint256 newInterest = source.principalAmount.getInterest(source.aprBps, block.timestamp - source.startTime);
uint256 tax = source.principalAmount.mulDivUp(taxBps, _PRECISION);
uint256 thisProtocolFee = 0;
uint256 thisTaxFee = 0;
if (withProtocolFee) {
thisProtocolFee = newInterest.mulDivUp(protocolFee.fraction, _PRECISION);
thisTaxFee = tax.mulDivUp(protocolFee.fraction, _PRECISION);
totalProtocolFee += thisProtocolFee + thisTaxFee;
}
uint256 repayment =
source.principalAmount + source.accruedInterest + newInterest - thisProtocolFee + tax - thisTaxFee;
asset.safeTransferFrom(loan.borrower, source.lender, repayment);
totalRepayment += repayment;
unchecked {
++i;
}
}
emit LoanRepaid(loanId, totalRepayment, totalProtocolFee);
if (withProtocolFee) {
asset.safeTransferFrom(loan.borrower, protocolFee.recipient, totalProtocolFee);
}
/// @dev Reclaim space.
delete _loans[loanId];
}
/// @inheritdoc IMultiSourceLoan
function liquidateLoan(uint256 _loanId, Loan calldata _loan)
external
override
nonReentrant
returns (bytes memory)
{
if (_loan.hash() != _loans[_loanId]) {
revert InvalidLoanError(_loanId);
}
uint256 expirationTime = _loan.startTime + _loan.duration;
address collateralAddress = _loan.nftCollateralAddress;
ERC721 collateralCollection = ERC721(collateralAddress);
if (expirationTime > block.timestamp) {
revert LoanNotDueError(expirationTime);
}
bytes memory liquidation;
if (_loan.source.length == 1) {
collateralCollection.transferFrom(address(this), _loan.source[0].lender, _loan.nftCollateralTokenId);
emit LoanForeclosed(_loanId);
/// @dev Reclaim space.
delete _loans[_loanId];
} else {
collateralCollection.transferFrom(address(this), address(_loanLiquidator), _loan.nftCollateralTokenId);
liquidation = _loanLiquidator.liquidateLoan(
_loanId,
collateralAddress,
_loan.nftCollateralTokenId,
_loan.principalAddress,
_liquidationAuctionDuration,
msg.sender
);
emit LoanSentToLiquidator(_loanId, address(_loanLiquidator));
}
return liquidation;
}
/// @inheritdoc IMultiSourceLoan
function loanLiquidated(uint256 _loanId, Loan calldata _loan) external override onlyLiquidator {
if (_loan.hash() != _loans[_loanId]) {
revert InvalidLoanError(_loanId);
}
emit LoanLiquidated(_loanId);
/// @dev Reclaim space.
delete _loans[_loanId];
}
function getMinSourcePrincipal(uint256 _loanPrincipal) external view returns (uint256) {
return _getMinSourcePrincipal(_loanPrincipal);
}
/// @inheritdoc IMultiSourceLoan
function delegate(uint256 _loanId, Loan calldata loan, address _delegate, bytes32 _rights, bool _value) external {
if (loan.hash() != _loans[_loanId]) {
revert InvalidLoanError(_loanId);
}
if (msg.sender != loan.borrower) {
revert OnlyBorrowerCallableError();
}
_delegateRegistry.delegateERC721(
_delegate, loan.nftCollateralAddress, loan.nftCollateralTokenId, _rights, _value
);
emit Delegated(_loanId, _delegate, _value);
}
/// @inheritdoc IMultiSourceLoan
function revokeDelegate(address _delegate, address _collection, uint256 _tokenId) external {
if (ERC721(_collection).ownerOf(_tokenId) == address(this)) {
revert InvalidMethodError();
}
_delegateRegistry.delegateERC721(_delegate, _collection, _tokenId, "", false);
emit RevokeDelegate(_delegate, _collection, _tokenId);
}
/// @inheritdoc IMultiSourceLoan
function getDelegateRegistry() external view returns (address) {
return address(_delegateRegistry);
}
/// @inheritdoc IMultiSourceLoan
function setDelegateRegistry(address _newDelegateRegistry) external onlyOwner {
_delegateRegistry = IDelegateRegistry(_newDelegateRegistry);
emit DelegateRegistryUpdated(_newDelegateRegistry);
}
/// @inheritdoc IMultiSourceLoan
function getMaxSources() external view returns (uint256) {
return _maxSources;
}
/// @inheritdoc IMultiSourceLoan
function setMaxSources(uint256 __maxSources) external onlyOwner {
_maxSources = __maxSources;
emit MaxSourcesUpdated(__maxSources);
}
/// @inheritdoc IMultiSourceLoan
function getMinLockPeriod() external view returns (uint256) {
return _minLockPeriod;
}
/// @inheritdoc IMultiSourceLoan
function setMinLockPeriod(uint256 __minLockPeriod) external onlyOwner {
_minLockPeriod = __minLockPeriod;
emit MinLockPeriodUpdated(__minLockPeriod);
}
/// @inheritdoc IMultiSourceLoan
function getLoanHash(uint256 _loanId) external view returns (bytes32) {
return _loans[_loanId];
}
/// @inheritdoc IMultiSourceLoan
function executeFlashAction(uint256 _loanId, Loan calldata _loan, address _target, bytes calldata _data) external {
if (_loan.hash() != _loans[_loanId]) {
revert InvalidLoanError(_loanId);
}
if (msg.sender != _loan.borrower) {
revert OnlyBorrowerCallableError();
}
ERC721(_loan.nftCollateralAddress).transferFrom(
address(this), address(_flashActionContract), _loan.nftCollateralTokenId
);
_flashActionContract.execute(_loan.nftCollateralAddress, _loan.nftCollateralTokenId, _target, _data);
if (ERC721(_loan.nftCollateralAddress).ownerOf(_loan.nftCollateralTokenId) != address(this)) {
revert NFTNotReturnedError();
}
emit FlashActionExecuted(_loanId, _target, _data);
}
/// @inheritdoc IMultiSourceLoan
function getFlashActionContract() external view returns (address) {
return address(_flashActionContract);
}
/// @inheritdoc IMultiSourceLoan
function setFlashActionContract(address _newFlashActionContract) external onlyOwner {
_flashActionContract = INFTFlashAction(_newFlashActionContract);
emit FlashActionContractUpdated(_newFlashActionContract);
}
/// @notice Update old sources and return the total delta, accrued interest, new sources and
/// transfer the protocol fee.
/// @param _renegotiationOffer The refinance offer.
/// @param _loan The loan to be refinanced.
/// @param _isStrictlyBetter Every source's apr needs to be improved.
/// @return totalDelta The total delta is the sum of all deltas across existing sources. This must be equal
/// to the new supplied (the total principal cannot change).
/// @return totalAccruedInterest Total accrued interest across all sources paid.
/// @return totalNewSources Total new sources, including new lender, left after the refinance.
/// @return totalAnnualInterest Total annual interest across all sources.
function _processOldSources(
RenegotiationOffer calldata _renegotiationOffer,
Loan memory _loan,
bool _isStrictlyBetter
)
private
returns (uint256 totalDelta, uint256 totalAccruedInterest, uint256 totalNewSources, uint256 totalAnnualInterest)
{
/// @dev Bring var to memory
ProtocolFee memory protocolFee = _protocolFee;
uint256 totalProtocolFee = 0;
if (protocolFee.fraction > 0 && _renegotiationOffer.fee > 0) {
totalProtocolFee = _renegotiationOffer.fee.mulDivUp(protocolFee.fraction, _PRECISION);
}
totalNewSources = 1;
for (uint256 i = 0; i < _renegotiationOffer.targetPrincipal.length;) {
Source memory source = _loan.source[i];
uint256 targetPrincipal = _renegotiationOffer.targetPrincipal[i];
(
uint256 delta,
uint256 accruedInterest,
uint256 isNewSource,
uint256 annualInterest,
uint256 thisProtocolFee
) = _processOldSource(
_renegotiationOffer.lender,
_loan.principalAddress,
source,
_loan.startTime + _loan.duration,
targetPrincipal,
protocolFee
);
_checkSourceStrictly(_isStrictlyBetter, delta, source.aprBps, _renegotiationOffer.aprBps, _minimum.interest);
totalAnnualInterest += annualInterest;
totalDelta += delta;
totalAccruedInterest += accruedInterest;
totalProtocolFee += thisProtocolFee;
totalNewSources += isNewSource;
unchecked {
++i;
}
}
_handleProtocolFeeForFee(_loan.principalAddress, _renegotiationOffer.lender, totalProtocolFee, protocolFee);
}
/// @notice Process the current source during a renegotiation.
/// @param _lender The new lender.
/// @param _principalAddress The principal address of the loan.
/// @param _source The source to be processed.
/// @param _endTime The end time of the loan.
/// @param _targetPrincipal The target principal of the source.
/// @param protocolFee The protocol fee.
/// @return delta The delta between the old and new principal.
/// @return accruedInterest The accrued interest paid.
/// @return isNewSource Whether the source is kept.
/// @return annualInterest The total annual interest paid (times 10000 since we have it in BPS)
/// @return thisProtocolFee The protocol fee paid for this source.
function _processOldSource(
address _lender,
address _principalAddress,
Source memory _source,
uint256 _endTime,
uint256 _targetPrincipal,
ProtocolFee memory protocolFee
)
private
returns (
uint256 delta,
uint256 accruedInterest,
uint256 isNewSource,
uint256 annualInterest,
uint256 thisProtocolFee
)
{
uint256 unlockedTime = _getUnlockedTime(_source.startTime, _endTime);
if (unlockedTime > block.timestamp) {
revert SourceCannotBeRefinancedError(unlockedTime);
}
delta = _source.principalAmount - _targetPrincipal;
annualInterest = _source.principalAmount * _source.aprBps;
if (delta == 0) {
return (0, 0, 1, annualInterest, 0);
}
accruedInterest = delta.getInterest(_source.aprBps, block.timestamp - _source.startTime);
if (protocolFee.fraction > 0) {
thisProtocolFee = accruedInterest.mulDivUp(protocolFee.fraction, _PRECISION);
}
uint256 proportionalAccrued = _source.accruedInterest.mulDivDown(delta, _source.principalAmount);
if (_targetPrincipal > 0) {
_source.accruedInterest -= proportionalAccrued;
isNewSource = 1;
}
accruedInterest += proportionalAccrued;
ERC20(_principalAddress).safeTransferFrom(_lender, _source.lender, delta + accruedInterest - thisProtocolFee);
}
function _baseLoanChecks(uint256 _loanId, Loan memory _loan) private view {
if (_loan.hash() != _loans[_loanId]) {
revert InvalidLoanError(_loanId);
}
if (_loan.startTime + _loan.duration < block.timestamp) {
revert LoanExpiredError();
}
}
function _baseRenegotiationChecks(RenegotiationOffer calldata _renegotiationOffer, Loan memory _loan)
private
view
{
if (
(_renegotiationOffer.principalAmount == 0)
|| (_loan.source.length != _renegotiationOffer.targetPrincipal.length)
) {
revert InvalidRenegotiationOfferError();
}
if (block.timestamp > _renegotiationOffer.expirationTime) {
revert ExpiredRenegotiationOfferError(_renegotiationOffer.expirationTime);
}
uint256 renegotiationId = _renegotiationOffer.renegotiationId;
address lender = _renegotiationOffer.lender;
if (
isRenegotiationOfferCancelled[lender][renegotiationId]
|| lenderMinRenegotiationOfferId[lender] >= renegotiationId
) {
revert CancelledRenegotiationOfferError(lender, renegotiationId);
}
}
function _getSourceFromOffer(
RenegotiationOffer memory _renegotiationOffer,
uint256 _accruedInterest,
uint256 _loanId
) private view returns (Source memory) {
return Source({
loanId: _loanId,
lender: _renegotiationOffer.lender,
principalAmount: _renegotiationOffer.principalAmount,
accruedInterest: _accruedInterest,
startTime: block.timestamp,
aprBps: _renegotiationOffer.aprBps
});
}
function _getMinSourcePrincipal(uint256 _loanPrincipal) private view returns (uint256) {
return _loanPrincipal / (_MAX_RATIO_SOURCE_MIN_PRINCIPAL * _maxSources);
}
/// @notice Protocol fee for fees charged on offers/renegotationOffers.
/// @param _principalAddress The principal address of the loan.
/// @param _lender The lender of the loan.
/// @param _fee The fee to be charged.
/// @param protocolFee The protocol fee variable brought to memory.
function _handleProtocolFeeForFee(
address _principalAddress,
address _lender,
uint256 _fee,
ProtocolFee memory protocolFee
) private {
if (protocolFee.fraction > 0 && _fee > 0) {
ERC20(_principalAddress).safeTransferFrom(_lender, protocolFee.recipient, _fee);
}
}
/// @notice Check condition for strictly better sources
/// @param _isStrictlyBetter Whether the new source needs to be strictly better than the old one.
/// @param _delta The delta between the old and new principal. 0 if unchanged.
/// @param _currentAprBps The current apr of the source.
/// @param _targetAprBps The target apr of the source.
/// @param _minImprovement The minimum improvement required.
function _checkSourceStrictly(
bool _isStrictlyBetter,
uint256 _delta,
uint256 _currentAprBps,
uint256 _targetAprBps,
uint256 _minImprovement
) private pure {
/// @dev If _isStrictlyBetter is set, and the new apr is higher, then it'll underflow.
if (
_isStrictlyBetter && _delta > 0
&& ((_currentAprBps - _targetAprBps).mulDivDown(_PRECISION, _currentAprBps) < _minImprovement)
) {
revert InvalidRenegotiationOfferError();
}
}
function _getUnlockedTime(uint256 _sourceStartTime, uint256 _loanEndTime) private view returns (uint256) {
return _sourceStartTime + (_loanEndTime - _sourceStartTime).mulDivUp(_minLockPeriod, _PRECISION);
}
}// SPDX-License-Identifier: CC0-1.0
pragma solidity >=0.8.13;
/**
* @title IDelegateRegistry
* @custom:version 2.0
* @custom:author foobar (0xfoobar)
* @notice A standalone immutable registry storing delegated permissions from one address to another
*/
interface IDelegateRegistry {
/// @notice Delegation type, NONE is used when a delegation does not exist or is revoked
enum DelegationType {
NONE,
ALL,
CONTRACT,
ERC721,
ERC20,
ERC1155
}
/// @notice Struct for returning delegations
struct Delegation {
DelegationType type_;
address to;
address from;
bytes32 rights;
address contract_;
uint256 tokenId;
uint256 amount;
}
/// @notice Emitted when an address delegates or revokes rights for their entire wallet
event DelegateAll(address indexed from, address indexed to, bytes32 rights, bool enable);
/// @notice Emitted when an address delegates or revokes rights for a contract address
event DelegateContract(address indexed from, address indexed to, address indexed contract_, bytes32 rights, bool enable);
/// @notice Emitted when an address delegates or revokes rights for an ERC721 tokenId
event DelegateERC721(address indexed from, address indexed to, address indexed contract_, uint256 tokenId, bytes32 rights, bool enable);
/// @notice Emitted when an address delegates or revokes rights for an amount of ERC20 tokens
event DelegateERC20(address indexed from, address indexed to, address indexed contract_, bytes32 rights, uint256 amount);
/// @notice Emitted when an address delegates or revokes rights for an amount of an ERC1155 tokenId
event DelegateERC1155(address indexed from, address indexed to, address indexed contract_, uint256 tokenId, bytes32 rights, uint256 amount);
/// @notice Thrown if multicall calldata is malformed
error MulticallFailed();
/**
* ----------- WRITE -----------
*/
/**
* @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
* @param data The encoded function data for each of the calls to make to this contract
* @return results The results from each of the calls passed in via data
*/
function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
/**
* @notice Allow the delegate to act on behalf of `msg.sender` for all contracts
* @param to The address to act as delegate
* @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
* @param enable Whether to enable or disable this delegation, true delegates and false revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateAll(address to, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);
/**
* @notice Allow the delegate to act on behalf of `msg.sender` for a specific contract
* @param to The address to act as delegate
* @param contract_ The contract whose rights are being delegated
* @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
* @param enable Whether to enable or disable this delegation, true delegates and false revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateContract(address to, address contract_, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);
/**
* @notice Allow the delegate to act on behalf of `msg.sender` for a specific ERC721 token
* @param to The address to act as delegate
* @param contract_ The contract whose rights are being delegated
* @param tokenId The token id to delegate
* @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
* @param enable Whether to enable or disable this delegation, true delegates and false revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateERC721(address to, address contract_, uint256 tokenId, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);
/**
* @notice Allow the delegate to act on behalf of `msg.sender` for a specific amount of ERC20 tokens
* @dev The actual amount is not encoded in the hash, just the existence of a amount (since it is an upper bound)
* @param to The address to act as delegate
* @param contract_ The address for the fungible token contract
* @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
* @param amount The amount to delegate, > 0 delegates and 0 revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateERC20(address to, address contract_, bytes32 rights, uint256 amount) external payable returns (bytes32 delegationHash);
/**
* @notice Allow the delegate to act on behalf of `msg.sender` for a specific amount of ERC1155 tokens
* @dev The actual amount is not encoded in the hash, just the existence of a amount (since it is an upper bound)
* @param to The address to act as delegate
* @param contract_ The address of the contract that holds the token
* @param tokenId The token id to delegate
* @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
* @param amount The amount of that token id to delegate, > 0 delegates and 0 revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateERC1155(address to, address contract_, uint256 tokenId, bytes32 rights, uint256 amount) external payable returns (bytes32 delegationHash);
/**
* ----------- CHECKS -----------
*/
/**
* @notice Check if `to` is a delegate of `from` for the entire wallet
* @param to The potential delegate address
* @param from The potential address who delegated rights
* @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
* @return valid Whether delegate is granted to act on the from's behalf
*/
function checkDelegateForAll(address to, address from, bytes32 rights) external view returns (bool);
/**
* @notice Check if `to` is a delegate of `from` for the specified `contract_` or the entire wallet
* @param to The delegated address to check
* @param contract_ The specific contract address being checked
* @param from The cold wallet who issued the delegation
* @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
* @return valid Whether delegate is granted to act on from's behalf for entire wallet or that specific contract
*/
function checkDelegateForContract(address to, address from, address contract_, bytes32 rights) external view returns (bool);
/**
* @notice Check if `to` is a delegate of `from` for the specific `contract` and `tokenId`, the entire `contract_`, or the entire wallet
* @param to The delegated address to check
* @param contract_ The specific contract address being checked
* @param tokenId The token id for the token to delegating
* @param from The wallet that issued the delegation
* @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
* @return valid Whether delegate is granted to act on from's behalf for entire wallet, that contract, or that specific tokenId
*/
function checkDelegateForERC721(address to, address from, address contract_, uint256 tokenId, bytes32 rights) external view returns (bool);
/**
* @notice Returns the amount of ERC20 tokens the delegate is granted rights to act on the behalf of
* @param to The delegated address to check
* @param contract_ The address of the token contract
* @param from The cold wallet who issued the delegation
* @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
* @return balance The delegated balance, which will be 0 if the delegation does not exist
*/
function checkDelegateForERC20(address to, address from, address contract_, bytes32 rights) external view returns (uint256);
/**
* @notice Returns the amount of a ERC1155 tokens the delegate is granted rights to act on the behalf of
* @param to The delegated address to check
* @param contract_ The address of the token contract
* @param tokenId The token id to check the delegated amount of
* @param from The cold wallet who issued the delegation
* @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
* @return balance The delegated balance, which will be 0 if the delegation does not exist
*/
function checkDelegateForERC1155(address to, address from, address contract_, uint256 tokenId, bytes32 rights) external view returns (uint256);
/**
* ----------- ENUMERATIONS -----------
*/
/**
* @notice Returns all enabled delegations a given delegate has received
* @param to The address to retrieve delegations for
* @return delegations Array of Delegation structs
*/
function getIncomingDelegations(address to) external view returns (Delegation[] memory delegations);
/**
* @notice Returns all enabled delegations an address has given out
* @param from The address to retrieve delegations for
* @return delegations Array of Delegation structs
*/
function getOutgoingDelegations(address from) external view returns (Delegation[] memory delegations);
/**
* @notice Returns all hashes associated with enabled delegations an address has received
* @param to The address to retrieve incoming delegation hashes for
* @return delegationHashes Array of delegation hashes
*/
function getIncomingDelegationHashes(address to) external view returns (bytes32[] memory delegationHashes);
/**
* @notice Returns all hashes associated with enabled delegations an address has given out
* @param from The address to retrieve outgoing delegation hashes for
* @return delegationHashes Array of delegation hashes
*/
function getOutgoingDelegationHashes(address from) external view returns (bytes32[] memory delegationHashes);
/**
* @notice Returns the delegations for a given array of delegation hashes
* @param delegationHashes is an array of hashes that correspond to delegations
* @return delegations Array of Delegation structs, return empty structs for nonexistent or revoked delegations
*/
function getDelegationsFromHashes(bytes32[] calldata delegationHashes) external view returns (Delegation[] memory delegations);
/**
* ----------- STORAGE ACCESS -----------
*/
/**
* @notice Allows external contracts to read arbitrary storage slots
*/
function readSlot(bytes32 location) external view returns (bytes32);
/**
* @notice Allows external contracts to read an arbitrary array of storage slots
*/
function readSlots(bytes32[] calldata locations) external view returns (bytes32[] memory);
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE/LOGIC
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
function tokenURI(uint256 id) public view virtual returns (string memory);
/*//////////////////////////////////////////////////////////////
ERC721 BALANCE/OWNER STORAGE
//////////////////////////////////////////////////////////////*/
mapping(uint256 => address) internal _ownerOf;
mapping(address => uint256) internal _balanceOf;
function ownerOf(uint256 id) public view virtual returns (address owner) {
require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
}
function balanceOf(address owner) public view virtual returns (uint256) {
require(owner != address(0), "ZERO_ADDRESS");
return _balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
ERC721 APPROVAL STORAGE
//////////////////////////////////////////////////////////////*/
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
/*//////////////////////////////////////////////////////////////
ERC721 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 id) public virtual {
address owner = _ownerOf[id];
require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual {
require(from == _ownerOf[id], "WRONG_FROM");
require(to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
"NOT_AUTHORIZED"
);
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
unchecked {
_balanceOf[from]--;
_balanceOf[to]++;
}
_ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(
address from,
address to,
uint256 id
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
bytes calldata data
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 id) internal virtual {
require(to != address(0), "INVALID_RECIPIENT");
require(_ownerOf[id] == address(0), "ALREADY_MINTED");
// Counter overflow is incredibly unrealistic.
unchecked {
_balanceOf[to]++;
}
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint256 id) internal virtual {
address owner = _ownerOf[id];
require(owner != address(0), "NOT_MINTED");
// Ownership check above ensures no underflow.
unchecked {
_balanceOf[owner]--;
}
delete _ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
/*//////////////////////////////////////////////////////////////
INTERNAL SAFE MINT LOGIC
//////////////////////////////////////////////////////////////*/
function _safeMint(address to, uint256 id) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _safeMint(
address to,
uint256 id,
bytes memory data
) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721TokenReceiver {
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
uint256 internal constant MAX_UINT256 = 2**256 - 1;
uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// Divide x * y by the denominator.
z := div(mul(x, y), denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// If x * y modulo the denominator is strictly greater than 0,
// 1 is added to round up the division of x * y by the denominator.
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
switch x
case 0 {
switch n
case 0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.
let half := shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n := shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n := shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.
// Equivalent to iszero(eq(div(xx, x), x)) here.
if shr(128, x) {
revert(0, 0)
}
// Store x squared.
let xx := mul(x, x)
// Round to the nearest number.
let xxRound := add(xx, half)
// Revert if xx + half overflowed.
if lt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x := div(xxRound, scalar)
// If n is even:
if mod(n, 2) {
// Compute z * x.
let zx := mul(z, x)
// If z * x overflowed:
if iszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.
if iszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.
let zxRound := add(zx, half)
// Revert if zx + half overflowed.
if lt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z := div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let y := x // We start y at x, which will help us make our initial estimate.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// We check y >= 2^(k + 8) but shift right by k bits
// each branch to ensure that if x >= 256, then y >= 256.
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could
// get y in a tighter range. Currently, we will have y in [256, 256*2^16).
// We ensured y >= 256 so that the relative difference between y and y+1 is small.
// That's not possible if x < 256 but we can just verify those cases exhaustively.
// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
// There is no overflow risk here since y < 2^136 after the first branch above.
z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between
// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z := sub(z, lt(div(x, z), z))
}
}
function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Mod x by y. Note this will return
// 0 instead of reverting if y is zero.
z := mod(x, y)
}
}
function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Divide x by y. Note this will return
// 0 instead of reverting if y is zero.
r := div(x, y)
}
}
function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Add 1 to x * y if x % y > 0. Note this will
// return 0 instead of reverting if y is zero.
z := add(gt(mod(x, y), 0), div(x, y))
}
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
uint256 private locked = 1;
modifier nonReentrant() virtual {
require(locked == 1, "REENTRANCY");
locked = 2;
_;
locked = 1;
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
interface INFTFlashAction {
error InvalidOwnerError();
/// @notice Execute an arbitrary flash action on a given NFT. This contract owns it and must return it.
/// @param _collection The NFT collection.
/// @param _tokenId The NFT token ID.
/// @param _target The target contract.
/// @param _data The data to send to the target.
function execute(address _collection, uint256 _tokenId, address _target, bytes calldata _data) external;
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
import "./IBaseLoan.sol";
interface IMultiSourceLoan {
/// @param executionData Execution data.
/// @param lender Lender address.
/// @param borrower Address that owns the NFT and will take over the loan.
/// @param lenderOfferSignature Signature of the offer (signed by lender).
/// @param borrowerOfferSignature Signature of the offer (signed by borrower).
/// @param callbackData Whether to call the afterPrincipalTransfer callback
struct LoanExecutionData {
IBaseLoan.ExecutionData executionData;
address lender;
address borrower;
bytes lenderOfferSignature;
bytes borrowerOfferSignature;
}
/// @param loanId Loan ID.
/// @param callbackData Whether to call the afterNFTTransfer callback
/// @param shouldDelegate Whether to delegate ownership of the NFT (avoid seaport flags).
struct SignableRepaymentData {
uint256 loanId;
bytes callbackData;
bool shouldDelegate;
}
/// @param loan Loan.
/// @param borrowerLoanSignature Signature of the loan (signed by borrower).
struct LoanRepaymentData {
SignableRepaymentData data;
Loan loan;
bytes borrowerSignature;
}
/// @notice When a loan is initiated, there's one source, the original lender. After each refinance,
/// a new source is created to represent the new lender, and potentially others dissapear (if a tranche
/// is fully refinanced)
/// @dev No need to have principal address here since it's the same across all, so it can live in the Loan.
/// @param loanId Loan ID.
/// @param lender Lender for this given source.
/// @param principalAmount Principal Amount.
/// @param accruedInterest Accrued Interest.
/// @param startTime Start Time. Either the time at which the loan initiated / was refinanced.
/// @param aprBps APR in basis points.
struct Source {
uint256 loanId;
address lender;
uint256 principalAmount;
uint256 accruedInterest;
uint256 startTime;
uint256 aprBps;
}
/// @dev Principal Amount is equal to the sum of all sources principalAmount.
/// We keep it for caching purposes. Since we are not saving this on chain but the hash,
/// it does not have a huge impact on gas.
/// @param borrower Borrower.
/// @param nftCollateralTokenId NFT Collateral Token ID.
/// @param nftCollateralAddress NFT Collateral Address.
/// @param principalAddress Principal Address.
/// @param principalAmount Principal Amount.
/// @param startTime Start Time.
/// @param duration Duration.
/// @param source Sources
struct Loan {
address borrower;
uint256 nftCollateralTokenId;
address nftCollateralAddress;
address principalAddress;
uint256 principalAmount;
uint256 startTime;
uint256 duration;
Source[] source;
}
struct RenegotiationOffer {
uint256 renegotiationId;
uint256 loanId;
address lender;
uint256 fee;
uint256[] targetPrincipal;
uint256 principalAmount;
uint256 aprBps;
uint256 expirationTime;
uint256 duration;
}
/// @notice Call by the borrower when emiting a new loan.
/// @param _executionData Loan execution data.
/// @return loanId Loan ID.
/// @return loan Loan.
function emitLoan(LoanExecutionData calldata _executionData) external returns (uint256, Loan memory);
/// @notice Refinance whole loan (leaving just one source).
/// @param _renegotiationOffer Offer to refinance a loan.
/// @param _loan Current loan.
/// @param _renegotiationOfferSignature Signature of the offer.
/// @return loanId New Loan Id, New Loan.
function refinanceFull(
RenegotiationOffer calldata _renegotiationOffer,
Loan memory _loan,
bytes calldata _renegotiationOfferSignature
) external returns (uint256, Loan memory);
/// @notice Refinance a loan partially. It can only be called by the new lender
/// (they are always a strict improvement on apr).
/// @param _renegotiationOffer Offer to refinance a loan partially.
/// @param _loan Current loan.
/// @return loanId New Loan Id, New Loan.
function refinancePartial(RenegotiationOffer calldata _renegotiationOffer, Loan memory _loan)
external
returns (uint256, Loan memory);
/// @notice Repay loan. Interest is calculated pro-rata based on time. Lender is defined by nft ownership.
/// @param _repaymentData Repayment data.
function repayLoan(LoanRepaymentData calldata _repaymentData) external;
/// @notice Call when a loan is past its due date.
/// @param _loanId Loan ID.
/// @param _loan Loan.
/// @return Liquidation Struct of the liquidation.
function liquidateLoan(uint256 _loanId, Loan calldata _loan) external returns (bytes memory);
/// @return maxSources Max sources per loan.
function getMaxSources() external view returns (uint256);
/// @notice Update the maximum number of sources per loan.
/// @param maxSources Maximum number of sources.
function setMaxSources(uint256 maxSources) external;
/// @notice Set min lock period (in BPS).
/// @param _minLockPeriod Min lock period.
function setMinLockPeriod(uint256 _minLockPeriod) external;
/// @notice Get min lock period (in BPS).
/// @return minLockPeriod Min lock period.
function getMinLockPeriod() external view returns (uint256);
/// @notice Get delegation registry.
/// @return delegateRegistry Delegate registry.
function getDelegateRegistry() external view returns (address);
/// @notice Update delegation registry.
/// @param _newDelegationRegistry Delegation registry.
function setDelegateRegistry(address _newDelegationRegistry) external;
/// @notice Delegate ownership.
/// @param _loanId Loan ID.
/// @param _loan Loan.
/// @param _rights Delegation Rights. Empty for all.
/// @param _delegate Delegate address.
/// @param _value True if delegate, false if undelegate.
function delegate(uint256 _loanId, Loan calldata _loan, address _delegate, bytes32 _rights, bool _value) external;
/// @notice Anyone can reveke a delegation on an NFT that's no longer in escrow.
/// @param _delegate Delegate address.
/// @param _collection Collection address.
/// @param _tokenId Token ID.
function revokeDelegate(address _delegate, address _collection, uint256 _tokenId) external;
/// @notice Get Flash Action Contract.
/// @return flashActionContract Flash Action Contract.
function getFlashActionContract() external view returns (address);
/// @notice Update Flash Action Contract.
/// @param _newFlashActionContract Flash Action Contract.
function setFlashActionContract(address _newFlashActionContract) external;
/// @notice Get Loan Hash.
/// @param _loanId Loan ID.
/// @return loanHash Loan Hash.
function getLoanHash(uint256 _loanId) external view returns (bytes32);
/// @notice Transfer NFT to the flash action contract (expected use cases here are for airdrops and similar scenarios).
/// The flash action contract would implement specific interactions with given contracts.
/// Only the the borrower can call this function for a given loan. By the end of the transaction, the NFT must have
/// been returned to escrow.
/// @param _loanId Loan ID.
/// @param _loan Loan.
/// @param _target Target address for the flash action contract to interact with.
/// @param _data Data to be passed to be passed to the ultimate contract.
function executeFlashAction(uint256 _loanId, Loan calldata _loan, address _target, bytes calldata _data) external;
/// @notice Extend loan duration. Can only be called by the lender on loans that have just one source.
/// @param _loanId Loan ID.
/// @param _loan Loan.
/// @param _extension Extension in seconds.
/// @return newLoanId New Loan Id
/// @return newLoan New Loan.
function extendLoan(uint256 _loanId, Loan memory _loan, uint256 _extension)
external
returns (uint256, Loan memory);
/// @notice Called by the liquidator for accounting purposes.
/// @param _loanId The id of the loan.
/// @param _loan The loan object.
function loanLiquidated(uint256 _loanId, Loan calldata _loan) external;
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
/// @title Liquidates Collateral for Defaulted Loans
/// @author Florida St
/// @notice It liquidates collateral corresponding to defaulted loans
/// and sends back the proceeds to the loan contract for distribution.
interface ILoanLiquidator {
/// @notice Given a loan, it takes posession of the NFT and liquidates it.
/// @param _loanId The loan id.
/// @param _contract The loan contract address.
/// @param _tokenId The NFT id.
/// @param _asset The asset address.
/// @param _duration The liquidation duration.
/// @param _originator The address that trigger the liquidation.
/// @return encodedAuction Encoded struct.
function liquidateLoan(
uint256 _loanId,
address _contract,
uint256 _tokenId,
address _asset,
uint96 _duration,
address _originator
) external returns (bytes memory);
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
import "../../interfaces/loans/IMultiSourceLoan.sol";
import "../../interfaces/loans/IBaseLoan.sol";
import "../../interfaces/IAuctionLoanLiquidator.sol";
library Hash {
// keccak256("OfferValidator(address validator,bytes arguments)")
bytes32 private constant _VALIDATOR_HASH = 0x4def3e04bd42194484d5f8a5b268ec0df03b9d9d0402606fe3100023c5d79ac4;
// keccak256("LoanOffer(uint256 offerId,address lender,uint256 fee,address borrower,uint256 capacity,address nftCollateralAddress,uint256 nftCollateralTokenId,address principalAddress,uint256 principalAmount,uint256 aprBps,uint256 expirationTime,uint256 duration,OfferValidator[] validators)OfferValidator(address validator,bytes arguments)")
bytes32 private constant _LOAN_OFFER_HASH = 0x891e530ed2768a9decac48f4b7beec447f755ce23feeeeb952e429145b44ba91;
/// keccak256("ExecutionData(LoanOffer offer,uint256 tokenId,uint256 amount,uint256 expirationTime,bytes callbackData)LoanOffer(uint256 offerId,address lender,uint256 fee,address borrower,uint256 capacity,address nftCollateralAddress,uint256 nftCollateralTokenId,address principalAddress,uint256 principalAmount,uint256 aprBps,uint256 expirationTime,uint256 duration,OfferValidator[] validators)OfferValidator(address validator,bytes arguments)")
bytes32 private constant _EXECUTION_DATA_HASH = 0x7e90717662b6dd110797922ef6d6701d92bfd4164783966933e092ea21a74c5a;
/// keccak256("SignableRepaymentData(uint256 loanId,bytes callbackData,bool shouldDelegate)")
bytes32 private constant _SIGNABLE_REPAYMENT_DATA_HASH =
0x41277b3c1cbe08ea7bbdd10a13f24dc956f3936bf46526f904c73697d9958e0c;
// keccak256("Loan(address borrower,uint256 nftCollateralTokenId,address nftCollateralAddress,address principalAddress,uint256 principalAmount,uint256 startTime,uint256 duration,Source[] source)Source(uint256 loanId,address lender,uint256 principalAmount,uint256 accruedInterest,uint256 startTime,uint256 aprBps)")
bytes32 private constant _MULTI_SOURCE_LOAN_HASH =
0x35f73c5cb07b3fa605378d4f576769166fed212ec3813ac1f1d73ef1c537eb0e;
// keccak256("Source(uint256 loanId,address lender,uint256 principalAmount,uint256 accruedInterest,uint256 startTime,uint256 aprBps)")
bytes32 private constant _SOURCE_HASH = 0x8ca047c2f10359bf4a27bd2c623674be3801153b6b2646ba08593dc96ad7bb44;
/// keccak256("RenegotiationOffer(uint256 renegotiationId,uint256 loanId,address lender,uint256 fee,uint256[] targetPrincipal,uint256 principalAmount,uint256 aprBps,uint256 expirationTime,uint256 duration)")
bytes32 private constant _MULTI_RENEGOTIATION_OFFER_HASH =
0xdb613ea3383336cd787d929ccfc21ab7cd87bf1d588780c80ce5f970dd79c348;
/// keccak256("Auction(address loanAddress,uint256 loanId,uint256 highestBid,uint256 triggerFee,address highestBidder,uint96 duration,address asset,uint96 startTime,address originator,uint96 lastBidTime)")
bytes32 private constant _AUCTION_HASH = 0xd1912299766a3d3ca1ad2e2135d884e08d798009860146382d22f8c389905b34;
function hash(IBaseLoan.LoanOffer memory _loanOffer) internal pure returns (bytes32) {
bytes memory encodedValidators;
for (uint256 i = 0; i < _loanOffer.validators.length;) {
encodedValidators = abi.encodePacked(encodedValidators, _hashValidator(_loanOffer.validators[i]));
unchecked {
++i;
}
}
return keccak256(
abi.encode(
_LOAN_OFFER_HASH,
_loanOffer.offerId,
_loanOffer.lender,
_loanOffer.fee,
_loanOffer.borrower,
_loanOffer.capacity,
_loanOffer.nftCollateralAddress,
_loanOffer.nftCollateralTokenId,
_loanOffer.principalAddress,
_loanOffer.principalAmount,
_loanOffer.aprBps,
_loanOffer.expirationTime,
_loanOffer.duration,
keccak256(encodedValidators)
)
);
}
function hash(IBaseLoan.ExecutionData memory _executionData) internal pure returns (bytes32) {
return keccak256(
abi.encode(
_EXECUTION_DATA_HASH,
hash(_executionData.offer),
_executionData.tokenId,
_executionData.amount,
_executionData.expirationTime,
keccak256(_executionData.callbackData)
)
);
}
function hash(IMultiSourceLoan.SignableRepaymentData memory _repaymentData) internal pure returns (bytes32) {
return keccak256(
abi.encode(
_SIGNABLE_REPAYMENT_DATA_HASH,
_repaymentData.loanId,
keccak256(_repaymentData.callbackData),
_repaymentData.shouldDelegate
)
);
}
function hash(IMultiSourceLoan.Loan memory _loan) internal pure returns (bytes32) {
bytes memory sourceHashes;
for (uint256 i = 0; i < _loan.source.length;) {
sourceHashes = abi.encodePacked(sourceHashes, _hashSource(_loan.source[i]));
unchecked {
++i;
}
}
return keccak256(
abi.encode(
_MULTI_SOURCE_LOAN_HASH,
_loan.borrower,
_loan.nftCollateralTokenId,
_loan.nftCollateralAddress,
_loan.principalAddress,
_loan.principalAmount,
_loan.startTime,
_loan.duration,
keccak256(sourceHashes)
)
);
}
function hash(IMultiSourceLoan.RenegotiationOffer memory _refinanceOffer) internal pure returns (bytes32) {
bytes memory encodedPrincipals;
for (uint256 i = 0; i < _refinanceOffer.targetPrincipal.length;) {
encodedPrincipals = abi.encodePacked(encodedPrincipals, _refinanceOffer.targetPrincipal[i]);
unchecked {
++i;
}
}
return keccak256(
abi.encode(
_MULTI_RENEGOTIATION_OFFER_HASH,
_refinanceOffer.renegotiationId,
_refinanceOffer.loanId,
_refinanceOffer.lender,
_refinanceOffer.fee,
keccak256(encodedPrincipals),
_refinanceOffer.principalAmount,
_refinanceOffer.aprBps,
_refinanceOffer.expirationTime,
_refinanceOffer.duration
)
);
}
function hash(IAuctionLoanLiquidator.Auction memory _auction) internal pure returns (bytes32) {
return keccak256(
abi.encode(
_AUCTION_HASH,
_auction.loanAddress,
_auction.loanId,
_auction.highestBid,
_auction.triggerFee,
_auction.highestBidder,
_auction.duration,
_auction.asset,
_auction.startTime,
_auction.originator,
_auction.lastBidTime
)
);
}
function _hashSource(IMultiSourceLoan.Source memory _source) private pure returns (bytes32) {
return keccak256(
abi.encode(
_SOURCE_HASH,
_source.lender,
_source.principalAmount,
_source.accruedInterest,
_source.startTime,
_source.aprBps
)
);
}
function _hashValidator(IBaseLoan.OfferValidator memory _validator) private pure returns (bytes32) {
return keccak256(abi.encode(_VALIDATOR_HASH, _validator.validator, keccak256(_validator.arguments)));
}
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
import "@solmate/utils/FixedPointMathLib.sol";
import "../../interfaces/loans/IMultiSourceLoan.sol";
import "../../interfaces/loans/IBaseLoan.sol";
library Interest {
using FixedPointMathLib for uint256;
uint256 private constant _PRECISION = 10000;
uint256 private constant _SECONDS_PER_YEAR = 31536000;
function getInterest(IBaseLoan.LoanOffer memory _loanOffer) internal pure returns (uint256) {
return _getInterest(_loanOffer.principalAmount, _loanOffer.aprBps, _loanOffer.duration);
}
function getInterest(uint256 _amount, uint256 _aprBps, uint256 _duration) internal pure returns (uint256) {
return _getInterest(_amount, _aprBps, _duration);
}
function getTotalOwed(IMultiSourceLoan.Loan memory _loan, uint256 _timestamp) internal pure returns (uint256) {
uint256 owed = 0;
for (uint256 i = 0; i < _loan.source.length;) {
IMultiSourceLoan.Source memory source = _loan.source[i];
owed += source.principalAmount + source.accruedInterest
+ _getInterest(source.principalAmount, source.aprBps, _timestamp - source.startTime);
unchecked {
++i;
}
}
return owed;
}
function _getInterest(uint256 _amount, uint256 _aprBps, uint256 _duration) private pure returns (uint256) {
return _amount.mulDivUp(_aprBps * _duration, _PRECISION * _SECONDS_PER_YEAR);
}
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
import "../interfaces/IMulticall.sol";
/// @title Multicall
/// @author Florida St
/// @notice Base implementation for multicall.
abstract contract Multicall is IMulticall {
function multicall(bytes[] calldata data) external payable override returns (bytes[] memory results) {
results = new bytes[](data.length);
bool success;
for (uint256 i = 0; i < data.length;) {
//slither-disable-next-line calls-loop,delegatecall-loop
(success, results[i]) = address(this).delegatecall(data[i]);
if (!success) revert MulticallFailed(i, results[i]);
unchecked {
++i;
}
}
}
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
import "../../interfaces/callbacks/ILoanCallback.sol";
import "./BaseLoan.sol";
abstract contract WithCallbacks is BaseLoan {
struct Taxes {
uint128 buyTax;
uint128 sellTax;
}
event WhitelistedCallbackContractAdded(address contractAdded, Taxes tax);
event WhitelistedCallbackContractRemoved(address contractRemoved);
mapping(address => Taxes) private _callbackTaxes;
constructor(string memory _name, address __currencyManager, address __collectionManager)
BaseLoan(_name, __currencyManager, __collectionManager)
{}
/// @notice Add a whitelisted callback contract / update an existing one with different taxes.
/// @param _contract Address of the contract.
function addWhitelistedCallbackContract(address _contract, Taxes calldata _tax) external onlyOwner {
_checkAddressNotZero(_contract);
if (_tax.buyTax > _PRECISION || _tax.sellTax > _PRECISION) {
revert InvalidValueError();
}
_isWhitelistedCallbackContract[_contract] = true;
_callbackTaxes[_contract] = _tax;
emit WhitelistedCallbackContractAdded(_contract, _tax);
}
/// @notice Remove a whitelisted callback contract.
/// @param _contract Address of the contract.
function removeWhitelistedCallbackContract(address _contract) external onlyOwner {
_isWhitelistedCallbackContract[_contract] = false;
delete _callbackTaxes[_contract];
emit WhitelistedCallbackContractRemoved(_contract);
}
/// @return Whether a callback contract is whitelisted
function isWhitelistedCallbackContract(address _contract) external view returns (bool) {
return _isWhitelistedCallbackContract[_contract];
}
/// @notice Handle the afterPrincipalTransfer callback.
/// @param _loan Loan.
/// @param _callbackData Callback data.
/// @param _fee Fee.
/// @return buyTax
function _handleAfterPrincipalTransferCallback(
IMultiSourceLoan.Loan memory _loan,
bytes memory _callbackData,
uint256 _fee
) internal returns (uint128) {
if (_noCallback(_callbackData)) {
return 0;
}
if (
!_isWhitelistedCallbackContract[msg.sender]
|| ILoanCallback(msg.sender).afterPrincipalTransfer(_loan, _fee, _callbackData)
!= ILoanCallback.afterPrincipalTransfer.selector
) {
revert ILoanCallback.InvalidCallbackError();
}
return _callbackTaxes[msg.sender].buyTax;
}
/// @notice Handle the afterNFTTransfer callback.
/// @param _loan Loan.
/// @param _callbackData Callback data.
/// @return sellTax
function _handleAfterNFTTransferCallback(IMultiSourceLoan.Loan memory _loan, bytes calldata _callbackData)
internal
returns (uint128)
{
if (_noCallback(_callbackData)) {
return 0;
}
if (
!_isWhitelistedCallbackContract[msg.sender]
|| ILoanCallback(msg.sender).afterNFTTransfer(_loan, _callbackData)
!= ILoanCallback.afterNFTTransfer.selector
) {
revert ILoanCallback.InvalidCallbackError();
}
return _callbackTaxes[msg.sender].sellTax;
}
function _noCallback(bytes memory _callbackData) private pure returns (bool) {
return _callbackData.length == 0;
}
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
import "../../interfaces/ILoanLiquidator.sol";
/// @title Interface for Loans.
/// @author Florida St
/// @notice Basic Loan
interface IBaseLoan {
/// @notice Minimum improvement (in BPS) required for a strict improvement.
/// @param principalAmount Minimum delta of principal amount.
/// @param interest Minimum delta of interest.
/// @param duration Minimum delta of duration.
struct ImprovementMinimum {
uint256 principalAmount;
uint256 interest;
uint256 duration;
}
/// @notice Arbitrary contract to validate offers implementing `IBaseOfferValidator`.
/// @param validator Address of the validator contract.
/// @param arguments Arguments to pass to the validator.
struct OfferValidator {
address validator;
bytes arguments;
}
/// @notice Borrowers receive offers that are then validated.
/// @dev Setting the nftCollateralTokenId to 0 triggers validation through `validators`.
/// @param offerId Offer ID. Used for canceling/setting as executed.
/// @param lender Lender of the offer.
/// @param fee Origination fee.
/// @param borrower Borrower of the offer. Can be set to 0 (any borrower).
/// @param capacity Capacity of the offer.
/// @param nftCollateralAddress Address of the NFT collateral.
/// @param nftCollateralTokenId NFT collateral token ID.
/// @param principalAddress Address of the principal.
/// @param principalAmount Principal amount of the loan.
/// @param aprBps APR in BPS.
/// @param expirationTime Expiration time of the offer.
/// @param duration Duration of the loan in seconds.
/// @param validators Arbitrary contract to validate offers implementing `IBaseOfferValidator`.
struct LoanOffer {
uint256 offerId;
address lender;
uint256 fee;
address borrower;
uint256 capacity;
address nftCollateralAddress;
uint256 nftCollateralTokenId;
address principalAddress;
uint256 principalAmount;
uint256 aprBps;
uint256 expirationTime;
uint256 duration;
OfferValidator[] validators;
}
/// @notice Offer + necessary fields to execute a specific loan. This has a separate expirationTime to avoid
/// someone holding an offer and executing much later, without the borrower's awareness.
/// @param offer Loan offer. It can be executed potentially for multiple ids / amounts < principalAmount.
/// @param tokenId NFT collateral token ID.
/// @param amount The amount the borrower is willing to take (must be <= _loanOffer principalAmount)
/// @param expirationTime Expiration time of the signed offer by the borrower.
/// @param callbackData Data to pass to the callback.
struct ExecutionData {
LoanOffer offer;
uint256 tokenId;
uint256 amount;
uint256 expirationTime;
bytes callbackData;
}
/// @notice Recipient address and fraction of gains charged by the protocol.
struct ProtocolFee {
address recipient;
uint256 fraction;
}
/// @notice Total number of loans issued by this contract.
function getTotalLoansIssued() external view returns (uint256);
/// @notice Cancel offer for `msg.sender`. Each lender has unique offerIds.
/// @param _offerId Offer ID.
function cancelOffer(uint256 _offerId) external;
/// @notice Cancel multiple offers.
/// @param _offerIds Offer IDs.
function cancelOffers(uint256[] calldata _offerIds) external;
/// @notice Cancell all offers with offerId < _minOfferId
/// @param _minOfferId Minimum offer ID.
function cancelAllOffers(uint256 _minOfferId) external;
/// @notice Cancel renegotiation offer. Similar to offers.
/// @param _renegotiationId Renegotiation offer ID.
function cancelRenegotiationOffer(uint256 _renegotiationId) external;
/// @notice Cancel multiple renegotiation offers.
/// @param _renegotiationIds Renegotiation offer IDs.
function cancelRenegotiationOffers(uint256[] calldata _renegotiationIds) external;
/// @notice Cancell all renegotiation offers with renegotiationId < _minRenegotiationId
/// @param _minRenegotiationId Minimum renegotiation offer ID.
function cancelAllRenegotiationOffers(uint256 _minRenegotiationId) external;
/// @return protocolFee The Protocol fee.
function getProtocolFee() external view returns (ProtocolFee memory);
/// @return pendingProtocolFee The pending protocol fee.
function getPendingProtocolFee() external view returns (ProtocolFee memory);
/// @return protocolFeeSetTime Time when the protocol fee was set to be changed.
function getPendingProtocolFeeSetTime() external view returns (uint256);
/// @notice Kicks off the process to update the protocol fee.
/// @param _newProtocolFee New protocol fee.
function updateProtocolFee(ProtocolFee calldata _newProtocolFee) external;
/// @notice Set the protocol fee if enough notice has been given.
function setProtocolFee() external;
/// @return Liquidator contract address
function getLiquidator() external returns (address);
/// @notice Updates the liquidation contract.
/// @param loanLiquidator New liquidation contract.
function updateLiquidationContract(ILoanLiquidator loanLiquidator) external;
/// @notice Updates the auction duration for liquidations.
/// @param _newDuration New auction duration.
function updateLiquidationAuctionDuration(uint48 _newDuration) external;
/// @return auctionDuration Returns the auction's duration for liquidations.
function getLiquidationAuctionDuration() external returns (uint48);
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
import "./loans/IMultiSourceLoan.sol";
/// @title Liquidates Collateral for Defaulted Loans using English Auctions.
/// @author Florida St
/// @notice It liquidates collateral corresponding to defaulted loans
/// and sends back the proceeds to the loan contract for distribution.
interface IAuctionLoanLiquidator {
/// @notice The auction struct.
/// @param loanAddress The loan contract address.
/// @param loanId The loan id.
/// @param highestBid The highest bid.
/// @param highestBidder The highest bidder.
/// @param duration The auction duration.
/// @param asset The asset address.
/// @param startTime The auction start time.
/// @param originator The address that triggered the liquidation.
/// @param lastBidTime The last bid time.
struct Auction {
address loanAddress;
uint256 loanId;
uint256 highestBid;
uint256 triggerFee;
address highestBidder;
uint96 duration;
address asset;
uint96 startTime;
address originator;
uint96 lastBidTime;
}
/// @notice Add a loan contract to the list of accepted contracts.
/// @param _loanContract The loan contract to be added.
function addLoanContract(address _loanContract) external;
/// @notice Remove a loan contract from the list of accepted contracts.
/// @param _loanContract The loan contract to be removed.
function removeLoanContract(address _loanContract) external;
/// @return The loan contracts that are accepted by this liquidator.
function getValidLoanContracts() external view returns (address[] memory);
/// @notice Update liquidation distributor.
/// @param _liquidationDistributor The new liquidation distributor.
function updateLiquidationDistributor(address _liquidationDistributor) external;
/// @return liquidationDistributor The liquidation distributor address.
function getLiquidationDistributor() external view returns (address);
/// @notice Called by the owner to update the trigger fee.
/// @param triggerFee The new trigger fee.
function updateTriggerFee(uint256 triggerFee) external;
/// @return triggerFee The trigger fee.
function getTriggerFee() external view returns (uint256);
/// @notice When a bid is placed, the contract takes possesion of the bid, and
/// if there was a previous bid, it returns that capital to the original
/// bidder.
/// @param _contract The nft contract address.
/// @param _tokenId The nft id.
/// @param _auction The auction struct.
/// @param _bid The bid amount.
/// @return auction The updated auction struct.
function placeBid(address _contract, uint256 _tokenId, Auction memory _auction, uint256 _bid)
external
returns (Auction memory);
/// @notice On settlement, the NFT is sent to the highest bidder.
/// Calls loan liquidated for accounting purposes.
/// @param _auction The auction struct.
/// @param _loan The loan struct.
function settleAuction(Auction calldata _auction, IMultiSourceLoan.Loan calldata _loan) external;
/// @notice The contract has hashes of all auctions to save space (not the actual struct)
/// @param _contract The nft contract address.
/// @param _tokenId The nft id.
/// @return auctionHash The auction hash.
function getAuctionHash(address _contract, uint256 _tokenId) external view returns (bytes32);
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
interface IMulticall {
error MulticallFailed(uint256 i, bytes returndata);
/// @notice Call multiple functions in the contract. Revert if one of them fails, return results otherwise.
/// @param data Encoded function calls.
/// @return results The results of the function calls.
function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
import "../loans/IMultiSourceLoan.sol";
interface ILoanCallback {
error InvalidCallbackError();
/// @notice Called by the MSL contract after the principal of loan has been tranfered (when a loan is initiated)
/// but before it tries to transfer the NFT into escrow.
/// @param _loan The loan.
/// @param _fee The origination fee.
/// @param _executionData Execution data for purchase.
/// @return The bytes4 magic value.
function afterPrincipalTransfer(IMultiSourceLoan.Loan memory _loan, uint256 _fee, bytes calldata _executionData)
external
returns (bytes4);
/// @notice Call by the MSL contract after the NFT has been transfered to the borrower repaying the loan, but before
/// transfering the principal to the lender.
/// @param _loan The loan.
/// @param _executionData Execution data for the offer.
/// @return The bytes4 magic value.
function afterNFTTransfer(IMultiSourceLoan.Loan memory _loan, bytes calldata _executionData)
external
returns (bytes4);
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
import "@openzeppelin/utils/cryptography/ECDSA.sol";
import "@openzeppelin/utils/cryptography/MessageHashUtils.sol";
import "@openzeppelin/interfaces/IERC1271.sol";
import "@solmate/tokens/ERC721.sol";
import "@solmate/utils/FixedPointMathLib.sol";
import "../AddressManager.sol";
import "../utils/Hash.sol";
import "../../interfaces/loans/IBaseLoan.sol";
import "../../interfaces/validators/IBaseOfferValidator.sol";
import "../InputChecker.sol";
/// @title BaseLoan
/// @author Florida St
/// @notice Base implementation that we expect all loans to share. Offers can either be
/// for new loans or renegotiating existing ones.
/// Offers are signed off-chain.
/// Offers have a nonce associated that is used for cancelling and
/// marking as executed.
abstract contract BaseLoan is ERC721TokenReceiver, IBaseLoan, InputChecker, Owned {
using FixedPointMathLib for uint256;
using ECDSA for bytes32;
using MessageHashUtils for bytes32;
using Hash for LoanOffer;
using Hash for ExecutionData;
/// @notice Used in compliance with EIP712
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 public immutable INITIAL_DOMAIN_SEPARATOR;
uint256 public constant MAX_PROTOCOL_FEE = 2500;
uint256 public constant FEE_UPDATE_NOTICE = 30 days;
uint48 public constant MIN_AUCTION_DURATION = 1 days;
bytes4 private constant MAGICVALUE_1271 = 0x1626ba7e;
/// @notice Precision used for calculating interests.
uint256 internal constant _PRECISION = 10000;
/// @notice Minimum improvement (in BPS) required for a strict improvement.
ImprovementMinimum internal _minimum = ImprovementMinimum(500, 100, 100);
string public name;
/// @notice Duration of the auction when a loan defaults requires a liquidation.
uint48 internal _liquidationAuctionDuration = 3 days;
/// @notice Liquidator used defaulted loans that requires liquidation.
ILoanLiquidator internal _loanLiquidator;
/// @notice Protocol fee charged on gains.
ProtocolFee internal _protocolFee;
/// @notice Set as the target new protocol fee.
ProtocolFee internal _pendingProtocolFee;
/// @notice Set when the protocol fee updating mechanisms starts.
uint256 internal _pendingProtocolFeeSetTime;
/// @notice Total number of loans issued. Given it's a serial value, we use it
/// as loan id.
uint256 public override getTotalLoansIssued;
/// @notice Offer capacity
mapping(address => mapping(uint256 => uint256)) internal _used;
/// @notice Used for validate off chain maker offers / canceling one
mapping(address => mapping(uint256 => bool)) public isOfferCancelled;
/// @notice Used for validating off chain maker offers / canceling all
mapping(address => uint256) public minOfferId;
/// @notice Used in a similar way as `isOfferCancelled` to handle renegotiations.
mapping(address => mapping(uint256 => bool)) public isRenegotiationOfferCancelled;
/// @notice Used in a similar way as `minOfferId` to handle renegotiations.
mapping(address => uint256) public lenderMinRenegotiationOfferId;
/// @notice Loans are only denominated in whitelisted addresses. Within each struct,
/// we save those as their `uint` representation.
AddressManager internal immutable _currencyManager;
/// @notice Only whilteslited collections are accepted as collateral. Within each struct,
/// we save those as their `uint` representation.
AddressManager internal immutable _collectionManager;
/// @notice For security reasons we only allow a whitelisted set of callback contracts.
mapping(address => bool) internal _isWhitelistedCallbackContract;
event OfferCancelled(address lender, uint256 offerId);
event BorrowerOfferCancelled(address borrower, uint256 offerId);
event AllOffersCancelled(address lender, uint256 minOfferId);
event RenegotiationOfferCancelled(address lender, uint256 renegotiationId);
event AllRenegotiationOffersCancelled(address lender, uint256 minRenegotiationId);
event ProtocolFeeUpdated(ProtocolFee fee);
event ProtocolFeePendingUpdate(ProtocolFee fee);
event LoanSentToLiquidator(uint256 loanId, address liquidator);
event LoanLiquidated(uint256 loanId);
event LoanForeclosed(uint256 loanId);
event ImprovementMinimumUpdated(ImprovementMinimum minimum);
event LiquidationContractUpdated(address liquidator);
event LiquidationAuctionDurationUpdated(uint256 newDuration);
error InvalidValueError();
error LiquidatorOnlyError(address _liquidator);
error CancelledOrExecutedOfferError(address _lender, uint256 _offerId);
error CancelledRenegotiationOfferError(address _lender, uint256 _renegotiationId);
error ExpiredOfferError(uint256 _expirationTime);
error ExpiredRenegotiationOfferError(uint256 _expirationTime);
error LowOfferIdError(address _lender, uint256 _newMinOfferId, uint256 _minOfferId);
error LowRenegotiationOfferIdError(address _lender, uint256 _newMinRenegotiationOfferId, uint256 _minOfferId);
error CannotLiquidateError();
error LoanNotDueError(uint256 _expirationTime);
error InvalidLenderError();
error InvalidBorrowerError();
error ZeroDurationError();
error ZeroInterestError();
error InvalidSignatureError();
error InvalidLiquidationError();
error CurrencyNotWhitelistedError();
error CollectionNotWhitelistedError();
error InvalidProtocolFeeError(uint256 _fraction);
error TooEarlyError(uint256 _pendingProtocolFeeSetTime);
error MaxCapacityExceededError();
error InvalidLoanError(uint256 _loanId);
error InvalidCollateralIdError();
error OnlyLenderOrBorrowerCallableError();
error OnlyBorrowerCallableError();
error OnlyLenderCallableError();
error NotStrictlyImprovedError();
error InvalidAmountError(uint256 _amount, uint256 _principalAmount);
error InvalidDurationError();
constructor(string memory _name, address currencyManager, address collectionManager) Owned(tx.origin) {
name = _name;
_checkAddressNotZero(currencyManager);
_checkAddressNotZero(collectionManager);
_currencyManager = AddressManager(currencyManager);
_collectionManager = AddressManager(collectionManager);
_pendingProtocolFeeSetTime = type(uint256).max;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = _computeDomainSeparator();
}
modifier onlyLiquidator() {
if (msg.sender != address(_loanLiquidator)) {
revert LiquidatorOnlyError(address(_loanLiquidator));
}
_;
}
/// @notice Get the domain separator requried to comply with EIP-712.
function DOMAIN_SEPARATOR() public view returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : _computeDomainSeparator();
}
/// @return The minimum improvement for a loan to be considered strictly better.
function getImprovementMinimum() external view returns (ImprovementMinimum memory) {
return _minimum;
}
/// @notice Updates the minimum improvement for a loan to be considered strictly better.
/// Only the owner can call this function.
/// @param _newMinimum The new minimum improvement.
function updateImprovementMinimum(ImprovementMinimum calldata _newMinimum) external onlyOwner {
_minimum = _newMinimum;
emit ImprovementMinimumUpdated(_newMinimum);
}
/// @return Address of the currency manager.
function getCurrencyManager() external view returns (address) {
return address(_currencyManager);
}
/// @return Address of the collection manager.
function getCollectionManager() external view returns (address) {
return address(_collectionManager);
}
/// @inheritdoc IBaseLoan
function cancelOffer(uint256 _offerId) external {
address user = msg.sender;
isOfferCancelled[user][_offerId] = true;
emit OfferCancelled(user, _offerId);
}
/// @inheritdoc IBaseLoan
function cancelOffers(uint256[] calldata _offerIds) external virtual {
address user = msg.sender;
uint256 total = _offerIds.length;
for (uint256 i = 0; i < total;) {
uint256 offerId = _offerIds[i];
isOfferCancelled[user][offerId] = true;
emit OfferCancelled(user, offerId);
unchecked {
++i;
}
}
}
/// @inheritdoc IBaseLoan
function cancelAllOffers(uint256 _minOfferId) external virtual {
address user = msg.sender;
uint256 currentMinOfferId = minOfferId[user];
if (currentMinOfferId >= _minOfferId) {
revert LowOfferIdError(user, _minOfferId, currentMinOfferId);
}
minOfferId[user] = _minOfferId;
emit AllOffersCancelled(user, _minOfferId);
}
/// @inheritdoc IBaseLoan
function cancelRenegotiationOffer(uint256 _renegotiationId) external virtual {
address lender = msg.sender;
isRenegotiationOfferCancelled[lender][_renegotiationId] = true;
emit RenegotiationOfferCancelled(lender, _renegotiationId);
}
/// @inheritdoc IBaseLoan
function cancelRenegotiationOffers(uint256[] calldata _renegotiationIds) external virtual {
address lender = msg.sender;
uint256 total = _renegotiationIds.length;
for (uint256 i = 0; i < total;) {
uint256 renegotiationId = _renegotiationIds[i];
isRenegotiationOfferCancelled[lender][renegotiationId] = true;
emit RenegotiationOfferCancelled(lender, renegotiationId);
unchecked {
++i;
}
}
}
/// @inheritdoc IBaseLoan
function cancelAllRenegotiationOffers(uint256 _minRenegotiationId) external virtual {
address lender = msg.sender;
uint256 currentMinRenegotiationOfferId = lenderMinRenegotiationOfferId[lender];
if (currentMinRenegotiationOfferId >= _minRenegotiationId) {
revert LowRenegotiationOfferIdError(lender, _minRenegotiationId, currentMinRenegotiationOfferId);
}
lenderMinRenegotiationOfferId[lender] = _minRenegotiationId;
emit AllRenegotiationOffersCancelled(lender, _minRenegotiationId);
}
/// @notice Returns the remaining capacity for a given loan offer.
/// @param _lender The address of the lender.
/// @param _offerId The id of the offer.
/// @return The amount lent out.
function getUsedCapacity(address _lender, uint256 _offerId) external view returns (uint256) {
return _used[_lender][_offerId];
}
/// @inheritdoc IBaseLoan
function getProtocolFee() external view returns (ProtocolFee memory) {
return _protocolFee;
}
/// @inheritdoc IBaseLoan
function getPendingProtocolFee() external view returns (ProtocolFee memory) {
return _pendingProtocolFee;
}
/// @inheritdoc IBaseLoan
function getPendingProtocolFeeSetTime() external view returns (uint256) {
return _pendingProtocolFeeSetTime;
}
/// @inheritdoc IBaseLoan
function setProtocolFee() external onlyOwner {
if (block.timestamp < _pendingProtocolFeeSetTime + FEE_UPDATE_NOTICE) {
revert TooEarlyError(_pendingProtocolFeeSetTime);
}
_protocolFee = _pendingProtocolFee;
emit ProtocolFeeUpdated(_pendingProtocolFee);
}
/// @inheritdoc IBaseLoan
function updateProtocolFee(ProtocolFee calldata _newProtocolFee) external onlyOwner {
if (_newProtocolFee.fraction > MAX_PROTOCOL_FEE) {
revert InvalidProtocolFeeError(_newProtocolFee.fraction);
}
_checkAddressNotZero(_newProtocolFee.recipient);
_pendingProtocolFee = _newProtocolFee;
_pendingProtocolFeeSetTime = block.timestamp;
emit ProtocolFeePendingUpdate(_pendingProtocolFee);
}
/// @inheritdoc IBaseLoan
function getLiquidator() external view returns (address) {
return address(_loanLiquidator);
}
/// @inheritdoc IBaseLoan
function updateLiquidationContract(ILoanLiquidator loanLiquidator) external onlyOwner {
_checkAddressNotZero(address(loanLiquidator));
_loanLiquidator = loanLiquidator;
emit LiquidationContractUpdated(address(loanLiquidator));
}
/// @inheritdoc IBaseLoan
function updateLiquidationAuctionDuration(uint48 _newDuration) external onlyOwner {
if (_newDuration < MIN_AUCTION_DURATION) {
revert InvalidDurationError();
}
_liquidationAuctionDuration = _newDuration;
emit LiquidationAuctionDurationUpdated(_newDuration);
}
/// @inheritdoc IBaseLoan
function getLiquidationAuctionDuration() external view returns (uint48) {
return _liquidationAuctionDuration;
}
/// @notice Call when issuing a new loan to get/set a unique serial id.
/// @dev This id should never be 0.
/// @return The new loan id.
function _getAndSetNewLoanId() internal returns (uint256) {
unchecked {
return ++getTotalLoansIssued;
}
}
/// @notice Base ExecutionData Checks
/// @dev Note that we do not validate fee < principalAmount since this is done in the child class in this case.
/// @param _executionData Loan execution data.
/// @param _lender The lender.
/// @param _borrower The borrower.
/// @param _offerer The offerrer (either lender or borrower)
/// @param _lenderOfferSignature The signature of the lender of LoanOffer.
/// @param _borrowerOfferSignature The signature of the borrower of ExecutionData.
function _validateExecutionData(
ExecutionData calldata _executionData,
address _lender,
address _borrower,
address _offerer,
bytes calldata _lenderOfferSignature,
bytes calldata _borrowerOfferSignature
) internal {
address lender = _executionData.offer.lender;
address borrower = _executionData.offer.borrower;
LoanOffer calldata offer = _executionData.offer;
uint256 offerId = offer.offerId;
if (msg.sender != _lender) {
_checkSignature(lender, offer.hash(), _lenderOfferSignature);
}
if (msg.sender != _borrower) {
_checkSignature(_borrower, _executionData.hash(), _borrowerOfferSignature);
}
if (block.timestamp > offer.expirationTime) {
revert ExpiredOfferError(offer.expirationTime);
}
if (block.timestamp > _executionData.expirationTime) {
revert ExpiredOfferError(_executionData.expirationTime);
}
if (isOfferCancelled[_offerer][offerId] || (offerId <= minOfferId[_offerer])) {
revert CancelledOrExecutedOfferError(_offerer, offerId);
}
if (_executionData.amount > offer.principalAmount) {
revert InvalidAmountError(_executionData.amount, offer.principalAmount);
}
if (!_currencyManager.isWhitelisted(offer.principalAddress)) {
revert CurrencyNotWhitelistedError();
}
if (!_collectionManager.isWhitelisted(offer.nftCollateralAddress)) {
revert CollectionNotWhitelistedError();
}
if (lender != address(0) && (lender != _lender)) {
revert InvalidLenderError();
}
if (borrower != address(0) && (borrower != _borrower)) {
revert InvalidBorrowerError();
}
if (offer.duration == 0) {
revert ZeroDurationError();
}
if (offer.aprBps == 0) {
revert ZeroInterestError();
}
if ((offer.capacity > 0) && (_used[_offerer][offer.offerId] + _executionData.amount > offer.capacity)) {
revert MaxCapacityExceededError();
}
_checkValidators(offer, _executionData.tokenId);
}
/// @notice Check generic offer validators for a given offer or
/// an exact match if no validators are given. The validators
/// check is performed only if tokenId is set to 0.
/// Having one empty validator is used for collection offers (all IDs match).
/// @param _loanOffer The loan offer to check.
/// @param _tokenId The token ID to check.
function _checkValidators(LoanOffer calldata _loanOffer, uint256 _tokenId) internal {
uint256 offerTokenId = _loanOffer.nftCollateralTokenId;
if (_loanOffer.nftCollateralTokenId != 0) {
if (offerTokenId != _tokenId) {
revert InvalidCollateralIdError();
}
} else {
uint256 totalValidators = _loanOffer.validators.length;
if (totalValidators == 0 && _tokenId != 0) {
revert InvalidCollateralIdError();
} else if ((totalValidators == 1) && (_loanOffer.validators[0].validator == address(0))) {
return;
}
for (uint256 i = 0; i < totalValidators;) {
OfferValidator memory thisValidator = _loanOffer.validators[i];
IBaseOfferValidator(thisValidator.validator).validateOffer(
_loanOffer, _tokenId, thisValidator.arguments
);
unchecked {
++i;
}
}
}
}
/// @notice Check a signature is valid given a hash and signer.
/// @dev Comply with IERC1271 and EIP-712.
function _checkSignature(address _signer, bytes32 _hash, bytes calldata _signature) internal view {
bytes32 offerHash = DOMAIN_SEPARATOR().toTypedDataHash(_hash);
if (_signer.code.length > 0) {
if (IERC1271(_signer).isValidSignature(offerHash, _signature) != MAGICVALUE_1271) {
revert InvalidSignatureError();
}
} else {
address recovered = offerHash.recover(_signature);
if (_signer != recovered) {
revert InvalidSignatureError();
}
}
}
/// @dev Check whether an offer is strictly better than a loan/source.
function _checkStrictlyBetter(
uint256 _offerPrincipalAmount,
uint256 _loanPrincipalAmount,
uint256 _offerEndTime,
uint256 _loanEndTime,
uint256 _offerAprBps,
uint256 _loanAprBps,
uint256 _offerFee
) internal view {
ImprovementMinimum memory minimum = _minimum;
/// @dev If principal is increased, then we need to check net daily interest is better.
/// interestDelta = (_loanAprBps * _loanPrincipalAmount - _offerAprBps * _offerPrincipalAmount)
/// We already checked that all sources are strictly better.
/// We check that the duration is not decreased or the offer charges a fee.
if (
(
(_offerPrincipalAmount - _loanPrincipalAmount > 0)
&& (
(_loanAprBps * _loanPrincipalAmount - _offerAprBps * _offerPrincipalAmount).mulDivDown(
_PRECISION, _loanAprBps * _loanPrincipalAmount
) < minimum.interest
)
) || (_offerFee > 0) || (_offerEndTime < _loanEndTime)
) {
revert NotStrictlyImprovedError();
}
}
/// @notice Compute domain separator for EIP-712.
/// @return The domain separator.
function _computeDomainSeparator() private view returns (bytes32) {
return keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("2"),
block.chainid,
address(this)
)
);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*/
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
import "@solmate/auth/Owned.sol";
import "@solmate/utils/ReentrancyGuard.sol";
import "./InputChecker.sol";
/// @title AddressManager
/// @notice A contract that handles a whitelist of addresses and their indexes.
/// @dev We assume no more than 65535 addresses will be added to the directory.
contract AddressManager is InputChecker, Owned, ReentrancyGuard {
event AddressAdded(address address_added);
event AddressRemovedFromWhitelist(address address_removed);
event AddressWhitelisted(address address_whitelisted);
error AddressAlreadyAddedError(address _address);
error AddressNotAddedError(address _address);
mapping(address => uint16) private _directory;
mapping(uint16 => address) private _inverseDirectory;
mapping(address => bool) private _whitelist;
uint16 private _lastAdded;
constructor(address[] memory _original) Owned(tx.origin) {
uint256 total = _original.length;
for (uint256 i; i < total;) {
_add(_original[i]);
unchecked {
++i;
}
}
}
/// @notice Adds an address to the directory. If it already exists,
/// reverts. It assumes it's whitelisted.
function add(address _entry) external onlyOwner returns (uint16) {
return _add(_entry);
}
/// @notice Whitelist an address that's already part of the directory.
function addToWhitelist(address _entry) external onlyOwner {
if (_directory[_entry] == 0) {
revert AddressNotAddedError(_entry);
}
_whitelist[_entry] = true;
emit AddressWhitelisted(_entry);
}
/// @notice Removes an address from the whitelist. We still keep it
/// in the directory since this mapping is relevant across time.
/// @param _entry The address to remove from the whitelist.
function removeFromWhitelist(address _entry) external onlyOwner {
_whitelist[_entry] = false;
emit AddressRemovedFromWhitelist(_entry);
}
/// @param _address The address to get the index for.
/// @return The index for a given address.
function addressToIndex(address _address) external view returns (uint16) {
return _directory[_address];
}
/// @param _index The index to get the address for.
/// @return The address for a given index.
function indexToAddress(uint16 _index) external view returns (address) {
return _inverseDirectory[_index];
}
/// @param _entry The address to check if it's whitelisted.
/// @return Whether the address is whitelisted or not.
function isWhitelisted(address _entry) external view returns (bool) {
return _whitelist[_entry];
}
function _add(address _entry) private returns (uint16) {
_checkAddressNotZero(_entry);
if (_directory[_entry] != 0) {
revert AddressAlreadyAddedError(_entry);
}
unchecked {
++_lastAdded;
}
_directory[_entry] = _lastAdded;
_inverseDirectory[_lastAdded] = _entry;
_whitelist[_entry] = true;
emit AddressAdded(_entry);
return _lastAdded;
}
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
import "../loans/IBaseLoan.sol";
/// @title Interface for Loan Offer Validators.
/// @author Florida St
/// @notice Verify the given `_offer` is valid for `_tokenId` and `_validatorData`.
interface IBaseOfferValidator {
/// @notice Validate a loan offer.
function validateOffer(IBaseLoan.LoanOffer calldata _offer, uint256 _tokenId, bytes calldata _validatorData)
external;
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
/// @title InputChecker
/// @author Florida St
/// @notice Some basic input checks.
abstract contract InputChecker {
error AddressZeroError();
function _checkAddressNotZero(address _address) internal pure {
if (_address == address(0)) {
revert AddressZeroError();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event OwnershipTransferred(address indexed user, address indexed newOwner);
/*//////////////////////////////////////////////////////////////
OWNERSHIP STORAGE
//////////////////////////////////////////////////////////////*/
address public owner;
modifier onlyOwner() virtual {
require(msg.sender == owner, "UNAUTHORIZED");
_;
}
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(address _owner) {
owner = _owner;
emit OwnershipTransferred(address(0), _owner);
}
/*//////////////////////////////////////////////////////////////
OWNERSHIP LOGIC
//////////////////////////////////////////////////////////////*/
function transferOwnership(address newOwner) public virtual onlyOwner {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}// 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) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}{
"remappings": [
"@forge-std/=lib/forge-std/src/",
"@openzeppelin/=lib/openzeppelin-contracts/contracts/",
"@solmate/=lib/solmate/src/",
"@zora/=lib/v3/contracts/",
"@chainlink/=lib/chainlink/contracts/src/v0.8/",
"@delegate/=lib/delegate-registry/src/",
"test/=test/",
"@manifoldxyz/=lib/v3/node_modules/@manifoldxyz/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@rari-capital/=lib/v3/node_modules/@rari-capital/",
"chainlink/=lib/chainlink/",
"delegate-registry/=lib/delegate-registry/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"murky/=lib/delegate-registry/lib/murky/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin/=lib/delegate-registry/lib/openzeppelin-contracts/contracts/",
"solmate/=lib/solmate/src/",
"v3/=lib/v3/contracts/"
],
"optimizer": {
"enabled": true,
"runs": 2000
},
"viaIR": true,
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
},
"evmVersion": "paris",
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"loanLiquidator","type":"address"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"internalType":"struct IBaseLoan.ProtocolFee","name":"protocolFee","type":"tuple"},{"internalType":"address","name":"currencyManager","type":"address"},{"internalType":"address","name":"collectionManager","type":"address"},{"internalType":"uint256","name":"maxSources","type":"uint256"},{"internalType":"uint256","name":"minLockPeriod","type":"uint256"},{"internalType":"address","name":"delegateRegistry","type":"address"},{"internalType":"address","name":"flashActionContract","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressZeroError","type":"error"},{"inputs":[{"internalType":"address","name":"_lender","type":"address"},{"internalType":"uint256","name":"_offerId","type":"uint256"}],"name":"CancelledOrExecutedOfferError","type":"error"},{"inputs":[{"internalType":"address","name":"_lender","type":"address"},{"internalType":"uint256","name":"_renegotiationId","type":"uint256"}],"name":"CancelledRenegotiationOfferError","type":"error"},{"inputs":[],"name":"CannotLiquidateError","type":"error"},{"inputs":[],"name":"CollectionNotWhitelistedError","type":"error"},{"inputs":[],"name":"CurrencyNotWhitelistedError","type":"error"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[{"internalType":"uint256","name":"_expirationTime","type":"uint256"}],"name":"ExpiredOfferError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_expirationTime","type":"uint256"}],"name":"ExpiredRenegotiationOfferError","type":"error"},{"inputs":[],"name":"ExtensionNotAvailableError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_principalAmount","type":"uint256"}],"name":"InvalidAmountError","type":"error"},{"inputs":[],"name":"InvalidBorrowerError","type":"error"},{"inputs":[],"name":"InvalidCallbackError","type":"error"},{"inputs":[],"name":"InvalidCollateralIdError","type":"error"},{"inputs":[],"name":"InvalidDurationError","type":"error"},{"inputs":[],"name":"InvalidLenderError","type":"error"},{"inputs":[],"name":"InvalidLiquidationError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"}],"name":"InvalidLoanError","type":"error"},{"inputs":[],"name":"InvalidMethodError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_fraction","type":"uint256"}],"name":"InvalidProtocolFeeError","type":"error"},{"inputs":[],"name":"InvalidRenegotiationOfferError","type":"error"},{"inputs":[],"name":"InvalidSignatureError","type":"error"},{"inputs":[],"name":"InvalidValueError","type":"error"},{"inputs":[],"name":"LengthMismatchError","type":"error"},{"inputs":[{"internalType":"address","name":"_liquidator","type":"address"}],"name":"LiquidatorOnlyError","type":"error"},{"inputs":[],"name":"LoanExpiredError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_expirationTime","type":"uint256"}],"name":"LoanNotDueError","type":"error"},{"inputs":[{"internalType":"address","name":"_lender","type":"address"},{"internalType":"uint256","name":"_newMinOfferId","type":"uint256"},{"internalType":"uint256","name":"_minOfferId","type":"uint256"}],"name":"LowOfferIdError","type":"error"},{"inputs":[{"internalType":"address","name":"_lender","type":"address"},{"internalType":"uint256","name":"_newMinRenegotiationOfferId","type":"uint256"},{"internalType":"uint256","name":"_minOfferId","type":"uint256"}],"name":"LowRenegotiationOfferIdError","type":"error"},{"inputs":[],"name":"MaxCapacityExceededError","type":"error"},{"inputs":[{"internalType":"uint256","name":"minLockPeriod","type":"uint256"}],"name":"MinLockPeriodTooHighError","type":"error"},{"inputs":[{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"bytes","name":"returndata","type":"bytes"}],"name":"MulticallFailed","type":"error"},{"inputs":[],"name":"NFTNotReturnedError","type":"error"},{"inputs":[],"name":"NotStrictlyImprovedError","type":"error"},{"inputs":[],"name":"OnlyBorrowerCallableError","type":"error"},{"inputs":[],"name":"OnlyLenderCallableError","type":"error"},{"inputs":[],"name":"OnlyLenderOrBorrowerCallableError","type":"error"},{"inputs":[],"name":"PartialOfferCannotChangeDurationError","type":"error"},{"inputs":[],"name":"PartialOfferCannotHaveFeeError","type":"error"},{"inputs":[],"name":"RefinanceFullError","type":"error"},{"inputs":[{"internalType":"uint256","name":"minTimestamp","type":"uint256"}],"name":"SourceCannotBeRefinancedError","type":"error"},{"inputs":[{"internalType":"uint256","name":"sourcePrincipal","type":"uint256"},{"internalType":"uint256","name":"loanPrincipal","type":"uint256"}],"name":"TargetPrincipalTooLowError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_pendingProtocolFeeSetTime","type":"uint256"}],"name":"TooEarlyError","type":"error"},{"inputs":[{"internalType":"uint256","name":"sources","type":"uint256"}],"name":"TooManySourcesError","type":"error"},{"inputs":[],"name":"ZeroDurationError","type":"error"},{"inputs":[],"name":"ZeroInterestError","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"uint256","name":"minOfferId","type":"uint256"}],"name":"AllOffersCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"uint256","name":"minRenegotiationId","type":"uint256"}],"name":"AllRenegotiationOffersCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"BorrowerOfferCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newdelegateRegistry","type":"address"}],"name":"DelegateRegistryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"Delegated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newFlashActionContract","type":"address"}],"name":"FlashActionContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"FlashActionExecuted","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"interest","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"indexed":false,"internalType":"struct IBaseLoan.ImprovementMinimum","name":"minimum","type":"tuple"}],"name":"ImprovementMinimumUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newDuration","type":"uint256"}],"name":"LiquidationAuctionDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator","type":"address"}],"name":"LiquidationContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"offerId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"indexed":false,"internalType":"struct IMultiSourceLoan.Loan","name":"loan","type":"tuple"},{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"LoanEmitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldLoanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLoanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"indexed":false,"internalType":"struct IMultiSourceLoan.Loan","name":"loan","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"_extension","type":"uint256"}],"name":"LoanExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"LoanForeclosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"LoanLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"renegotiationId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldLoanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLoanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"indexed":false,"internalType":"struct IMultiSourceLoan.Loan","name":"loan","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"LoanRefinanced","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalRepayment","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"LoanRepaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"address","name":"liquidator","type":"address"}],"name":"LoanSentToLiquidator","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMax","type":"uint256"}],"name":"MaxSourcesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minLockPeriod","type":"uint256"}],"name":"MinLockPeriodUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"OfferCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"indexed":false,"internalType":"struct IBaseLoan.ProtocolFee","name":"fee","type":"tuple"}],"name":"ProtocolFeePendingUpdate","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"indexed":false,"internalType":"struct IBaseLoan.ProtocolFee","name":"fee","type":"tuple"}],"name":"ProtocolFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"uint256","name":"renegotiationId","type":"uint256"}],"name":"RenegotiationOfferCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"RevokeDelegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"contractAdded","type":"address"},{"components":[{"internalType":"uint128","name":"buyTax","type":"uint128"},{"internalType":"uint128","name":"sellTax","type":"uint128"}],"indexed":false,"internalType":"struct WithCallbacks.Taxes","name":"tax","type":"tuple"}],"name":"WhitelistedCallbackContractAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"contractRemoved","type":"address"}],"name":"WhitelistedCallbackContractRemoved","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_UPDATE_NOTICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INITIAL_DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PROTOCOL_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_AUCTION_DURATION","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"},{"components":[{"internalType":"uint128","name":"buyTax","type":"uint128"},{"internalType":"uint128","name":"sellTax","type":"uint128"}],"internalType":"struct WithCallbacks.Taxes","name":"_tax","type":"tuple"}],"name":"addWhitelistedCallbackContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minOfferId","type":"uint256"}],"name":"cancelAllOffers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minRenegotiationId","type":"uint256"}],"name":"cancelAllRenegotiationOffers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_offerId","type":"uint256"}],"name":"cancelOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_offerIds","type":"uint256[]"}],"name":"cancelOffers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_renegotiationId","type":"uint256"}],"name":"cancelRenegotiationOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_renegotiationIds","type":"uint256[]"}],"name":"cancelRenegotiationOffers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"loan","type":"tuple"},{"internalType":"address","name":"_delegate","type":"address"},{"internalType":"bytes32","name":"_rights","type":"bytes32"},{"internalType":"bool","name":"_value","type":"bool"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"components":[{"internalType":"uint256","name":"offerId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"capacity","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"},{"internalType":"uint256","name":"expirationTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"address","name":"validator","type":"address"},{"internalType":"bytes","name":"arguments","type":"bytes"}],"internalType":"struct IBaseLoan.OfferValidator[]","name":"validators","type":"tuple[]"}],"internalType":"struct IBaseLoan.LoanOffer","name":"offer","type":"tuple"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expirationTime","type":"uint256"},{"internalType":"bytes","name":"callbackData","type":"bytes"}],"internalType":"struct IBaseLoan.ExecutionData","name":"executionData","type":"tuple"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"bytes","name":"lenderOfferSignature","type":"bytes"},{"internalType":"bytes","name":"borrowerOfferSignature","type":"bytes"}],"internalType":"struct IMultiSourceLoan.LoanExecutionData","name":"_executionData","type":"tuple"}],"name":"emitLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"},{"internalType":"address","name":"_target","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"executeFlashAction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"},{"internalType":"uint256","name":"_extension","type":"uint256"}],"name":"extendLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getCollectionManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrencyManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDelegateRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFlashActionContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getImprovementMinimum","outputs":[{"components":[{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"interest","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"internalType":"struct IBaseLoan.ImprovementMinimum","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidationAuctionDuration","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"}],"name":"getLoanHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxSources","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMinLockPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanPrincipal","type":"uint256"}],"name":"getMinSourcePrincipal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPendingProtocolFee","outputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"internalType":"struct IBaseLoan.ProtocolFee","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPendingProtocolFeeSetTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProtocolFee","outputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"internalType":"struct IBaseLoan.ProtocolFee","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalLoansIssued","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_lender","type":"address"},{"internalType":"uint256","name":"_offerId","type":"uint256"}],"name":"getUsedCapacity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"isOfferCancelled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"isRenegotiationOfferCancelled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"isWhitelistedCallbackContract","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lenderMinRenegotiationOfferId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"}],"name":"liquidateLoan","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"}],"name":"loanLiquidated","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"minOfferId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"renegotiationId","type":"uint256"},{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256[]","name":"targetPrincipal","type":"uint256[]"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"},{"internalType":"uint256","name":"expirationTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"internalType":"struct IMultiSourceLoan.RenegotiationOffer","name":"_renegotiationOffer","type":"tuple"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"},{"internalType":"bytes","name":"_renegotiationOfferSignature","type":"bytes"}],"name":"refinanceFull","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"renegotiationId","type":"uint256"},{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256[]","name":"targetPrincipal","type":"uint256[]"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"},{"internalType":"uint256","name":"expirationTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"internalType":"struct IMultiSourceLoan.RenegotiationOffer","name":"_renegotiationOffer","type":"tuple"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"}],"name":"refinancePartial","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"removeWhitelistedCallbackContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"bytes","name":"callbackData","type":"bytes"},{"internalType":"bool","name":"shouldDelegate","type":"bool"}],"internalType":"struct IMultiSourceLoan.SignableRepaymentData","name":"data","type":"tuple"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"loan","type":"tuple"},{"internalType":"bytes","name":"borrowerSignature","type":"bytes"}],"internalType":"struct IMultiSourceLoan.LoanRepaymentData","name":"_repaymentData","type":"tuple"}],"name":"repayLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_delegate","type":"address"},{"internalType":"address","name":"_collection","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"revokeDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newDelegateRegistry","type":"address"}],"name":"setDelegateRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newFlashActionContract","type":"address"}],"name":"setFlashActionContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"__maxSources","type":"uint256"}],"name":"setMaxSources","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"__minLockPeriod","type":"uint256"}],"name":"setMinLockPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"interest","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"internalType":"struct IBaseLoan.ImprovementMinimum","name":"_newMinimum","type":"tuple"}],"name":"updateImprovementMinimum","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"_newDuration","type":"uint48"}],"name":"updateLiquidationAuctionDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ILoanLiquidator","name":"loanLiquidator","type":"address"}],"name":"updateLiquidationContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"internalType":"struct IBaseLoan.ProtocolFee","name":"_newProtocolFee","type":"tuple"}],"name":"updateProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6101008060405234620005175780620064eb803803809162000022828562000538565b8339810103906101208212620005175760406200003f826200055c565b92601f19011262000517576040519162000059836200051c565b62000067602083016200055c565b83526040820151602084015262000081606083016200055c565b9262000090608084016200055c565b9160a08401519160c085015193620000ba610100620000b260e089016200055c565b97016200055c565b96604051620000c9816200051c565b601781527f474f4e44495f4d554c54495f534f555243455f4c4f414e000000000000000000602082015260016000553260018060a01b031960015416176001556040513260007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a3606081016001600160401b03811182821017620003ad57604052606460406101f4928381528260208201520152600255606460035560646004556203f48065ffffffffffff19600654161760065580519060018060401b038211620003ad578190620001a160055462000571565b601f8111620004c1575b50602090601f8311600114620004435760009262000437575b50508160011b916000199060031b1c1916176005555b620001e581620005ae565b620001f082620005ae565b6001600160a01b0390811660c0521660e052600019600b5546608052604051600554816000620002208362000571565b8083529260018116908115620004165750600114620003c3575b620002489250038262000538565b60208151910120916040519260208401907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f825260408501527fad7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a560608501524660808501523060a085015260a0845260c084019380851060018060401b03861117620003ad5760209460405251902060a052620002e581620005ae565b60068054600160301b600160d01b03191660309290921b600160301b600160d01b03169190911790558051600780546001600160a01b03199081166001600160a01b03938416179091559290910151600855601592909255601692909255601780548316938216939093179092556018805490911692909116919091179055604051615ef99081620005d28239608051816145be015260a05181818161283101526145e5015260c051818181611c2a0152612c49015260e051818181610f9d0152611cb10152f35b634e487b7160e01b600052604160045260246000fd5b50600560009081529091600080516020620064cb8339815191525b818310620003f957505090602062000248928201016200023a565b6020919350806001915483858801015201910190918392620003de565b602092506200024894915060ff191682840152151560051b8201016200023a565b015190503880620001c4565b600560009081529350600080516020620064cb83398151915291905b601f1984168510620004a5576001945083601f198116106200048b575b505050811b01600555620001da565b015160001960f88460031b161c191690553880806200047c565b818101518355602094850194600190930192909101906200045f565b90915060056000526020600020601f840160051c8101602085106200050f575b90849392915b601f830160051c82018110620004ff575050620001ab565b60008155859450600101620004e7565b5080620004e1565b600080fd5b604081019081106001600160401b03821117620003ad57604052565b601f909101601f19168101906001600160401b03821190821017620003ad57604052565b51906001600160a01b03821682036200051757565b90600182811c92168015620005a3575b60208310146200058d57565b634e487b7160e01b600052602260045260246000fd5b91607f169162000581565b6001600160a01b031615620005bf57565b6040516349431df560e11b8152600490fdfe61014080604052600436101561001457600080fd5b600090813560e01c90816306fdde0314613fc157508063150b7a0214613f5157806316ec9bc614613f195780631c31cac314613e9a5780632513a5ae146136c45780632891433d1461368c5780632c0a286e146130ef5780633644e515146130d45780633687121b146130ad57806344af681c1461308f5780634ae25a8614612fd05780634ec8198114612f745780634fd930ba14612ef75780635122afb114612e1c5780635448608614612c94578063584d158f14612c6d5780635a32989414612c29578063665458a714612be257806367562c641461285457806367e22404146128195780636ccc9dde146127625780637320ca261461270657806373b99f101461262657806373c2390c146126005780637795960b1461254d5780637b8d1a0b146125235780637d4e41ed14611a5b5780638617780b14611a3d5780638da5cb5b14611a16578063912d819e146119cf578063931c56e9146119b1578063986a7ace14611934578063995fe7201461190d578063a5a41031146118b6578063aa29dad11461188c578063ab250a3a146117cd578063ac9650d814611618578063acb1dfdb146115d6578063b1f0c786146112e7578063b8ca3b83146112ca578063b97e527a14611203578063baab19941461118e578063c2f50a7a14611170578063c833796d14611152578063cc37ef4f14611115578063cf23797d146110f7578063da144a5114611040578063db540a8a14610fc1578063dd7d978214610f7d578063e51e104014610dbd578063e621ffa914610cf4578063e69d8c4714610c8d578063ed21cbd214610ae4578063ed5a42841461095f578063ef706adf146108dc578063f0ad206a1461088d578063f2fde38b146108125763f95cc78f146102a057600080fd5b3461080f576003196040813601126108075767ffffffffffffffff90816004351161080b57610120906004353603011261080757602435908111610807576102ec9036906004016142d9565b6102f4614820565b5060808101610303815161583d565b60a46004350135106107c957506001600160a01b036103266044600435016147d2565b16330361079f5761033d8160246004350135615674565b61034c816004356004016156d8565b61010460043501356107755760646004350135918261074b57600092839081610373614774565b95839260208801519081151580610744575b610731575b5050919290600182935b6103a8608460043501600435600401614c65565b90508510156104ec576103bf8560e08a01516145a5565b5160a061041d8b6103e4896103de608460043501600435600401614c65565b90614764565b358c856103f56044600435016147d2565b916104176001600160a01b036060830151169160c08982015191015190614799565b92615537565b9296919490950151600354841515918261049a575b50506104705760019561045861045e946104526104649861045895614799565b9a614799565b99614799565b93614799565b94019390949291610394565b60046040517f25444587000000000000000000000000000000000000000000000000000000008152fd5b9091506104ad60c460043501358261497c565b612710907e068db8bac710cb295e9e1b089a027525460aa64c2f837b4a2339c0ebedfa4381118202158302156104e7570204103880610432565b600080fd5b508686926105178a886001600160a01b036060860151166105116044600435016147d2565b90615877565b60a4600435013503610470576015548211610700576001600c54019384600c5584601f1961055d6105478661426a565b95610555604051978861417b565b80875261426a565b0160005b8181106106e45750506105829161057d366004356004016153aa565b6157ed565b61058b83614598565b5261059582614598565b50600193835b6105af608460043501600435600401614c65565b9050811015610639576105d0816103de608460043501600435600401614c65565b356105de575b60010161059b565b94600180916105f18860e08701516145a5565b516105fc82886145a5565b5261060781876145a5565b50610620886103de608460043501600435600401614c65565b35604061062d83896145a5565b510152019590506105d6565b507f9190f728135a4ce2ee0c985cc2c2ce55169f358d6a979e6d48add4743482621c848460e085015261066b84615d37565b83825260146020526040822055602460043501358152806040812055604051809160043560040135825260246004350135602083015284604083015260a060608301526106bb60a08301876143c9565b9060808301520390a16106e060405192839283526040602084015260408301906143c9565b0390f35b60209192506106f161490e565b82828801015201908691610561565b602482604051907fc56ee9910000000000000000000000000000000000000000000000000000000082526004820152fd5b61073c9294506149e6565b91388061038a565b5085610385565b60046040517fe791b76b000000000000000000000000000000000000000000000000000000008152fd5b60046040517f32259e57000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3f9b6963000000000000000000000000000000000000000000000000000000008152fd5b60449051604051907f2e67bbe300000000000000000000000000000000000000000000000000000000825260a4600435013560048301526024820152fd5b5080fd5b8280fd5b80fd5b503461080f57602060031936011261080f5773ffffffffffffffffffffffffffffffffffffffff196108426141e6565b600154906001600160a01b039061085c82841633146144fe565b169182911617600155337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b503461080f578060031936011261080f57604080516108ab816140da565b828152826020820152015260606108c0614740565b6040805191805183526020810151602084015201516040820152f35b503461080f57602060031936011261080f577f899cab278284ae4a91172caa0943607a0bcb19766254c3ebe1139be00650b102610959600435338452600e6020526040842081855260205260408420600160ff198254161790556040519182913383602090939291936001600160a01b0360408201951681520152565b0390a180f35b503461080f57606060031936011261080f576109796141e6565b60407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc360112610807576001600160a01b036109ba816001541633146144fe565b6109c3826147e6565b6fffffffffffffffffffffffffffffffff9161271080846109e26158b9565b1611908115610ad1575b50610aa7571690818352601260205260408320600160ff1982541617905560136020526040832081610a1c6158b9565b16815490857fffffffffffffffffffffffffffffffff00000000000000000000000000000000610a4a6158d8565b60801b169216171790556040519182526024358181168091036104e75760208301526044359081168091036104e7578160609160407f58b1f18b665c4c540aef11f1c3dec18c43824ab0e2e8057a7a9bc6ad3653be24940152a180f35b60046040517fb53b7b15000000000000000000000000000000000000000000000000000000008152fd5b905083610adc6158d8565b1611386109ec565b503461080f5760031960a081360112610807576004356024359167ffffffffffffffff8311610c895761010083600401918436030112610c8957610b26614212565b90608435918215158093036104e757610b47610b4236846142d9565b615d37565b9184875260209260148452604088205403610c715790610b71916001600160a01b039283916147d2565b163303610c47578160a484928983601754166024610b9160448d016147d2565b9b604051988997889663b18e2bbb60e01b8852169d8e600488015216828601520135604484015260643560648401528860848401525af18015610c3c57610c0d575b50907f696a99e04dd3d11d9ec4fe68917db2c142d3a55e1bff074c34ad74c1d515547d93606093926040519384528301526040820152a180f35b9080939291813d8311610c35575b610c25818361417b565b810103126104e757909138610bd3565b503d610c1b565b6040513d88823e3d90fd5b60046040517fdbd91fb4000000000000000000000000000000000000000000000000000000008152fd5b6024856040519063047e261360e21b82526004820152fd5b8380fd5b503461080f578060031936011261080f5760006020604051610cae81614143565b82815201526106e0604051610cc281614143565b6009546001600160a01b0316808252600a54602092830190815260408051928352905192820192909252918291820190565b503461080f57610d03366144c7565b6001600160a01b0360065460301c16803303610d8c5750610b42610d289136906142d9565b8183526014602052604083205403610d74577faf91ffe368225045d42f7c2b4b90feb438133ac1e375e73bc9066652b70a1d776020604051838152a18152601460205280604081205580f35b6024906040519063047e261360e21b82526004820152fd5b602490604051907f8afaa9b50000000000000000000000000000000000000000000000000000000082526004820152fd5b503461080f57606060031936011261080f576004359060243567ffffffffffffffff811161080757610df39036906004016142d9565b91604435610dff614820565b50610e0a8483615674565b60e0840191600183515111610f53576080610e258451614598565b51015192610e5d60a087015194610e57610e4e82610e4960c08c01998a5190614799565b61497c565b601654906149e6565b90614799565b428111610f2257506020610e796001600160a01b039251614598565b51015116330361079f5782610eb0837f611e850fd4909c086538320a1f747a380fdaff020cc4e6649b9b4dd0dbd383219551614799565b90526001600c54019384600c55610ec686615d37565b858252601460205260408220558181526040812055604051918291825284602083015260806040830152610efd60808301876143c9565b9060608301520390a16106e060405192839283526040602084015260408301906143c9565b602490604051907f4d321dfb0000000000000000000000000000000000000000000000000000000082526004820152fd5b60046040517fcd23b291000000000000000000000000000000000000000000000000000000008152fd5b503461080f578060031936011261080f5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461080f57602060031936011261080f577faed358a2bd4ca37fb72362cad5ae66b0b49a2a2031f9474a656e47b1f4afc4196020610ffe6141e6565b6001600160a01b0390611016826001541633146144fe565b168073ffffffffffffffffffffffffffffffffffffffff196018541617601855604051908152a180f35b503461080f57602060031936011261080f5760043533825260116020526040822054818110156110b55750338083526011602090815260408085208490558051928352908201929092527f5009c2e03e218242def2d459330d4f8b8b7eb7289ca08c47489aeef06672c63c9181908101610959565b6040517f997e86d600000000000000000000000000000000000000000000000000000000815233600482015260248101929092526044820152606490fd5b0390fd5b503461080f578060031936011261080f576020601654604051908152f35b503461080f57602060031936011261080f5760ff60406020926001600160a01b0361113e6141e6565b168152601284522054166040519015158152f35b503461080f578060031936011261080f576020601554604051908152f35b503461080f578060031936011261080f576020604051620151808152f35b503461080f57606060031936011261080f576111b66001600160a01b036001541633146144fe565b7f36e7ba2b68a5f2a92d31c35c23faa8f6fb2559d4406bb30eef08bdbab0d6f10960606004358060025560243580600355604435908160045560405192835260208301526040820152a180f35b503461080f57602060031936011261080f5760043565ffffffffffff81168091036108075761123e6001600160a01b036001541633146144fe565b6201518081106112a0576020817f9b0306e96c09148e30f9acd9a1ebc2c7cb1bc0348adbf320530ead875f78292c927fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000006006541617600655604051908152a180f35b60046040517fa922f92c000000000000000000000000000000000000000000000000000000008152fd5b503461080f578060031936011261080f5760206040516109c48152f35b503461080f576003196080813601126108075767ffffffffffffffff6004356024358281116115d257610100816004019482360301126115d257611329614212565b926064359081116115ce5761134290369060040161423c565b919094611352610b4236836142d9565b84885260146020526040882054036115b657611378906001600160a01b039182916147d2565b163303610c475760448201878261138e836147d2565b16936024846018541691013594803b1561080b576040516323b872dd60e01b81523060048201526001600160a01b039290921660248301526044820186905282908290606490829084905af180156115ab57611597575b50508160185416886113f6836147d2565b823b1561080757899885938484809461145b604051988996879586947fee534a3f0000000000000000000000000000000000000000000000000000000086521660048501528d6024850152169e8f6044840152608060648401528d6084840191614c9b565b03925af191821561158a57849261156f575b505061147a6020926147d2565b16926024604051809581937f6352211e00000000000000000000000000000000000000000000000000000000835260048301525afa918215611564578792611534575b503091160361150a577f13ab7606a034e1d4320c1129442ffa929fe2789f8cd0d03d147ff4f4707567a5936109599160405194859485526020850152606060408501526060840191614c9b565b60046040517f807a2b8f000000000000000000000000000000000000000000000000000000008152fd5b61155691925060203d811161155d575b61154e818361417b565b810190615518565b90386114bd565b503d611544565b6040513d89823e3d90fd5b61157a9192506140f6565b6115865781883861146d565b8780fd5b50604051903d90823e3d90fd5b6115a0906140f6565b6115865787386113e5565b6040513d84823e3d90fd5b6024846040519063047e261360e21b82526004820152fd5b8580fd5b8480fd5b503461080f57604060031936011261080f5760406020916001600160a01b036115fd6141e6565b168152600d8352818120602435825283522054604051908152f35b506020806003193601126108075760043567ffffffffffffffff811161080b5761164783913690600401614496565b6116508161426a565b9161165e604051938461417b565b818352601f1961166d8361426a565b01845b8181106117be575050835b8281106116ff5750505060405191838301848452825180915260408401948060408360051b870101940192955b8287106116b55785850386f35b9091929382806116ef837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08a6001960301865288516141c1565b96019201960195929190926116a8565b84806117138360059998991b850185614549565b9081604051928392833781018381520390305af43d156117b7573d6117378161457c565b90611745604051928361417b565b81523d888883013e5b61175883876145a5565b52156117695760010194939461167b565b61177381856145a5565b51906110f36040519283927f6cf73d1400000000000000000000000000000000000000000000000000000000845260048401526040602484015260448301906141c1565b606061174e565b60608582018801528601611670565b503461080f576020806003193601126108075760043567ffffffffffffffff811161080b57611800903690600401614496565b835b81811061180d578480f35b611818818385614764565b3590338652601085526040862082875285527fa39a039982552bc613a549c5a31d7c9a911b4e57c7da274435fca0e156c9c34061188360408820936001948560ff198254161790556040519182913383602090939291936001600160a01b0360408201951681520152565b0390a101611802565b503461080f57602060031936011261080f5760406020916004358152601483522054604051908152f35b503461080f578060031936011261080f57600060206040516118d781614143565b82815201526106e06118e7614774565b6040519182918291909160208060408301946001600160a01b0381511684520151910152565b503461080f578060031936011261080f5760206001600160a01b0360175416604051908152f35b503461080f57602060031936011261080f577fa39a039982552bc613a549c5a31d7c9a911b4e57c7da274435fca0e156c9c34061095960043533845260106020526040842081855260205260408420600160ff198254161790556040519182913383602090939291936001600160a01b0360408201951681520152565b503461080f578060031936011261080f57602060405162278d008152f35b503461080f57604060031936011261080f5760ff60406020926001600160a01b036119f86141e6565b16815260108452818120602435825284522054166040519015158152f35b503461080f578060031936011261080f5760206001600160a01b0360015416604051908152f35b503461080f578060031936011261080f576020600c54604051908152f35b503461080f576003199060208236011261080f576004359167ffffffffffffffff83116108075760a090833603011261080f57611a96614820565b50611aa4600182541461485d565b60028155611ab4602483016147d2565b611ac0604484016147d2565b91611ad7611ad160048601806148a8565b806148db565b936001600160a01b03611aec602087016147d2565b1661251c57835b611b0060048301806148a8565b94611b116064840184600401614549565b9690611b236084860186600401614549565b611b386020611b3286806148db565b016147d2565b91611b486060611b3287806148db565b93611b5386806148db565b9b8c916001600160a01b038d1633036124f8575b5050506001600160a01b03871633036123b9575b50506101408901358042116123885750606083013580421161238857506001600160a01b0384168752600e602052604087208935885260205260ff6040882054168015612368575b61232757610100890135806040850135116122ed5750611be560e08a016147d2565b6001600160a01b03604051917f3af32abf0000000000000000000000000000000000000000000000000000000083521660048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156122995788916122ce575b50156122a457611c6c60a08a016147d2565b6001600160a01b03604051917f3af32abf0000000000000000000000000000000000000000000000000000000083521660048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa90811561229957889161226a575b5015612240576001600160a01b0316801515908161222c575b50612202576001600160a01b031680151590816121ee575b506121c4576101608701351561219a57610120870135156121705760808701358015159081612139575b5061210f5760209687611d5192013590614cbc565b6001600c54019485600c556040611d6e85600401866004016148a8565b013593611d79614940565b604051611d858161410a565b8881526001600160a01b0384168a8201528660408201528760608201524260808201526101208b013560a0820152611dbc82614598565b52611dc681614598565b506001600160a01b0389611ddd60048501806148a8565b0135818c611df960e0611df260a084016147d2565b92016147d2565b926040519d8e611e0881614126565b848c16815201521660408c01521660608a01528560808a01524260a08a01526101608a013560c08a015260e0890152611e4088615d37565b87875260146020526040872055610100890135856000190460408b0135118602158102156104e757899086604083013502908082049106151501611e82614774565b611e8e60e084016147d2565b91602082019282878551611ea290856149e6565b90611eac93615877565b611eb860e085016147d2565b6001600160a01b03168689611ecd848d61497c565b91611ed793614a09565b611ee460048601806148a8565b60808101611ef191614549565b3690611efc92614989565b90611f07918d6158f7565b6fffffffffffffffffffffffffffffffff168061209d575b505050611f466001600160a01b03611f3b60a0602094016147d2565b1692600401806148a8565b0135813b15612099576040516323b872dd60e01b81526001600160a01b038616600482015230602482015260448101919091529086908290606490829084905af18015610c3c57612085575b50877f2b81fc2f914e99b38053262ace9e49b2f47f712ef594be1ea005de4f3baeec9d88886080946001600160a01b0360019b9c9d359881611fec60405196879687528c602088015260c0604088015260c08701906143c9565b931660608501521686830152604085013560a08301520390a1013515612055576001600160a01b03168352600d6020526040832090835260205261203560408320918254614799565b90555b556106e060405192839283526040602084015260408301906143c9565b6001600160a01b03919250168252600e60205260408220908252602052604081208260ff19825416179055612038565b61208f86916140f6565b6115d25738611f92565b8680fd5b6120b56120ad6120dd928b6149e6565b9351846149e6565b9286896120d7866001600160a01b036120d060e08b016147d2565b169461497c565b92614a09565b868215611f1f576001600160a01b03806120fc60e061210697016147d2565b1692511691614a09565b88388086611f1f565b60046040517fdab69d5e000000000000000000000000000000000000000000000000000000008152fd5b90506001600160a01b0383168652600d602052604086208835875260205261216960408301356040882054614799565b1138611d3c565b60046040517f20dac71b000000000000000000000000000000000000000000000000000000008152fd5b60046040517fecd83f88000000000000000000000000000000000000000000000000000000008152fd5b60046040517f1548268b000000000000000000000000000000000000000000000000000000008152fd5b90506001600160a01b038416141538611d12565b60046040517f62257e48000000000000000000000000000000000000000000000000000000008152fd5b90506001600160a01b038816141538611cfa565b60046040517ff1a638e6000000000000000000000000000000000000000000000000000000008152fd5b61228c915060203d602011612292575b612284818361417b565b810190614c4d565b38611ce1565b503d61227a565b6040513d8a823e3d90fd5b60046040517fa212b5d3000000000000000000000000000000000000000000000000000000008152fd5b6122e7915060203d60201161229257612284818361417b565b38611c5a565b8360449160408051927f111c5f29000000000000000000000000000000000000000000000000000000008452013560048301526024820152fd5b6040517ffc4a5c250000000000000000000000000000000000000000000000000000000081526001600160a01b038516600482015289356024820152604490fd5b506001600160a01b0384168752600f602052604087205489351115611bc3565b602490604051907f5929d5bb0000000000000000000000000000000000000000000000000000000082526004820152fd5b60a0853603126124f4576040518060a081011067ffffffffffffffff60a0830111176124de5760a0810160405267ffffffffffffffff8635116124da576124033687358801614b04565b815260208601356020820152604086013560408201526060860135606082015267ffffffffffffffff6080870135116124da57906124d3929161244c3660808901358901614a99565b6080820190815261245d8251615b6f565b9160208101519160606040830151920151905160208151910120916040519360208501957f7e90717662b6dd110797922ef6d6701d92bfd4164783966933e092ea21a74c5a875260408601526060850152608084015260a083015260c082015260c081526124ca8161415f565b51902088615095565b3880611b7b565b8980fd5b634e487b7160e01b600052604160045260246000fd5b8880fd5b61250e612509612514943690614b04565b615b6f565b86615095565b8a3880611b67565b8293611af3565b503461080f578060031936011261080f5760206001600160a01b0360065460301c16604051908152f35b503461080f57602060031936011261080f57600435338252600f6020526040822054818110156125c2575033808352600f602090815260408085208490558051928352908201929092527feaacd06746bfd850a83bbe929f94f4e1413f9aac82dc76e4331418aaf8bba4689181908101610959565b6040517f570f5b7800000000000000000000000000000000000000000000000000000000815233600482015260248101929092526044820152606490fd5b503461080f578060031936011261080f57602065ffffffffffff60065416604051908152f35b503461080f578060031936011261080f576001600160a01b0361264e816001541633146144fe565b600b5462278d0081018082116126f25742106126c1575060407fb3c1d38dbdc9199d0ce01f386d70e29014ed4af7af1d321ca6641a91f4b4dc0c91600954168073ffffffffffffffffffffffffffffffffffffffff196007541617600755600a548060085582519182526020820152a180f35b602490604051907f5c269db50000000000000000000000000000000000000000000000000000000082526004820152fd5b602484634e487b7160e01b81526011600452fd5b503461080f57602060031936011261080f577fe818ff6972bf8970ca14a893539e57452588b197b76e6afe29c40a31b3363c6160206004356127546001600160a01b036001541633146144fe565b80601655604051908152a180f35b503461080f57602060031936011261080f576004356001600160a01b03808216808303610c89577f0656dc0fa3e59bca53331c14b87a4778ffac8c184915152fdd0f103838a4f378926127bc6020936001541633146144fe565b6127c5826147e6565b7fffffffffffff0000000000000000000000000000000000000000ffffffffffff79ffffffffffffffffffffffffffffffffffffffff0000000000006006549260301b16911617600655604051908152a180f35b503461080f578060031936011261080f5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461080f57612863366144c7565b91612871600182541461485d565b60028155612882610b4236856142d9565b9180825260209260148452604083205403610d74576128a960c085013560a0860135614799565b936001600160a01b0394856128c0604084016147d2565b1690428111612bb1575060609560e083019060016128de83866154e2565b9050146000146129d257506128f390836154e2565b156129be578561290391016147d2565b91813b156115d2576040516323b872dd60e01b81523060048201526001600160a01b03939093166024840152850135604483015283908290606490829084905af180156129b35761299f575b50906001917f5985c1ea46d5ed2be4837f88c0563e18ed022c3bfe10dee15f6ce74daf897d6384604051838152a18152601483528060408120555b556106e06040519282849384528301906141c1565b6129a983916140f6565b610807573861294f565b6040513d85823e3d90fd5b602485634e487b7160e01b81526032600452fd5b96919050600654918684013592823b15612099576040516323b872dd60e01b815230600482015260309190911c89166001600160a01b0316602482015260448101849052868160648183875af180156115645791899188959493612b94575b5060c49291612a45869260065498016147d2565b968160405198899788967f762d7d740000000000000000000000000000000000000000000000000000000088528c60048901526024880152604487015216606485015265ffffffffffff811660848501523360a485015260301c165af19081156129b3578391612af0575b506040600193927f0c0f58e3bcc7b4b6f90dac7e83ad948f2dfe00aff9393c51aa2181fa08fb94e1929660065460301c16825191825286820152a161298a565b90503d8084833e612b01818361417b565b8101908481830312610c895780519067ffffffffffffffff82116115d2570181601f82011215610c8957805190612b378261457c565b92612b45604051948561417b565b8284528683830101116115d2579282612b8a6040936001979689807f0c0f58e3bcc7b4b6f90dac7e83ad948f2dfe00aff9393c51aa2181fa08fb94e19801910161419e565b9250929350612ab0565b612ba29193949592506140f6565b6115ce57908786939238612a31565b602490604051907fb73a6a130000000000000000000000000000000000000000000000000000000082526004820152fd5b503461080f57604060031936011261080f5760ff60406020926001600160a01b03612c0b6141e6565b168152600e8452818120602435825284522054166040519015158152f35b503461080f578060031936011261080f5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461080f578060031936011261080f5760206001600160a01b0360185416604051908152f35b503461080f57606060031936011261080f57612cae6141e6565b612cb66141fc565b604435916001600160a01b0380831690604051917f6352211e0000000000000000000000000000000000000000000000000000000083528560048401526020928381602481855afa908115612299578891612dff575b5082163014612dd557829060a483601754169389604051958694859363b18e2bbb60e01b85528a16600485015260248401528a6044840152600060648401528160848401525af18015610c3c57612dac575b5050604080516001600160a01b0392831681529290911660208301528101919091527f6ebfd923dee76d83a8a77f3f29fe17755e74e5322d953678e35381cb63709199908060608101610959565b813d8311612dce575b612dbf818361417b565b810103126104e7573880612d5e565b503d612db5565b60046040517f64436547000000000000000000000000000000000000000000000000000000008152fd5b612e169150843d861161155d5761154e818361417b565b38612d0c565b503461080f57604060031936011261080f576001600160a01b03612e45816001541633146144fe565b602435906109c48211612ec6577f160fef22fef07b45037a807beef2c89408a81168d1055cf34024c85396b882af91604091612e87612e826147bc565b6147e6565b612e8f6147bc565b16908173ffffffffffffffffffffffffffffffffffffffff19600954161760095580600a5542600b5582519182526020820152a180f35b602482604051907f8b902c630000000000000000000000000000000000000000000000000000000082526004820152fd5b503461080f57602060031936011261080f577f924773930b2679ab8bf328330b211bedfb8f917551fd856f536bec008d17f9a66020612f346141e6565b6001600160a01b0390612f4c826001541633146144fe565b16808452601282526040842060ff19815416905560138252836040812055604051908152a180f35b503461080f57602060031936011261080f577f759c526639cdac7b56455b1bb335d0edb0ea487d414f1fa706cedf5532f09fc46020600435612fc26001600160a01b036001541633146144fe565b80601555604051908152a180f35b503461080f576020806003193601126108075760043567ffffffffffffffff811161080b57613003903690600401614496565b835b818110613010578480f35b61301b818385614764565b3590338652600e85526040862082875285527f899cab278284ae4a91172caa0943607a0bcb19766254c3ebe1139be00650b10261308660408820936001948560ff198254161790556040519182913383602090939291936001600160a01b0360408201951681520152565b0390a101613005565b503461080f578060031936011261080f576020600b54604051908152f35b503461080f57602060031936011261080f5760206130cc60043561583d565b604051908152f35b503461080f578060031936011261080f5760206130cc6145b9565b503461080f576003199060208236011261080f5767ffffffffffffffff6004351161080f57606060043536039283011261080f57613130600182541461485d565b600281556131426004803501806154cd565b35907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefd60246004350135930183121561080f576001600160a01b0361318c600485813501016147d2565b16330361356a575b6131ad6131a736600486813501016142d9565b83615674565b60406131bd6004803501806154cd565b01358015158103610807576134b9575b6001600160a01b036131e560448560043501016147d2565b166131f5600485813501016147d2565b813b1561080b576040516323b872dd60e01b8152306004808301919091526001600160a01b0392909216602480830191909152913586019091013560448201529082908290606490829084905af180156115ab576134aa575b5061328361326d6132636004803501806154cd565b6020810190614549565b9061327e36600488813501016142d9565b615a38565b9261328c614774565b93602085015115159183906001600160a01b036132af60648360043501016147d2565b16859386945b6132c960048035860160e4810191016154e2565b9050861015613425576132e660048035860160e4810191016154e2565b87101561340f576132fd90369060c0890201614282565b9087613329604084015161332360a086015161331d60808801514261497c565b906154ba565b906149c0565b8c61334a6fffffffffffffffffffffffffffffffff871660408701516149e6565b918c918d946133b5575b508593610e496133ad969461338261338295610e4961338796613382604060019e015160608d015190614799565b614799565b80936001600160a01b0360206133a260048d813501016147d2565b920151169089614a09565b9501946132b5565b6001969450613382925099610e496133ad969461338284610e49613387969f6133f06133e960206133fb93015180956149e6565b93866149e6565b96610e578885614799565b9f965050955050949650509395508e613354565b634e487b7160e01b600052603260045260246000fd5b888486858d8b7fd9cfbcfb9ae5d65b8d34072de4575f602ee25cf3715bc05c291701fe9e258f9a60608f98604051908982526020820152866040820152a161347e575b8585815260146020528060408120556001815580f35b6001600160a01b0361349860046134a196813501016147d2565b91511691614a09565b82808080613468565b6134b3906140f6565b3861324e565b6001600160a01b036017541660206001600160a01b0360a46134e0600488813501016147d2565b856134f160448a60043501016147d2565b9584604051978896879563b18e2bbb60e01b875216600486015216602484015260248a6004350101356044840152816064840152600160848401525af180156115ab5761353f575b506131cd565b602090813d8311613563575b613555818361417b565b8101031261080f5738613539565b503d61354b565b613579600484813501016147d2565b6135876004803501806154cd565b60608136031261080b576040519061359e826140da565b80358252602081013567ffffffffffffffff81116115d2576135c39036908301614a99565b6020830181905291604082013580151590036115d2579060409182820135838201525192602081519101208251937f41277b3c1cbe08ea7bbdd10a13f24dc956f3936bf46526f904c73697d9958e0c6020860152838501526060840152013515156080820152608081528060a081011067ffffffffffffffff60a0830111176136785760a0810160405280516020909101206136739161366b60048035604481019101614549565b929091615095565b613194565b602483634e487b7160e01b81526041600452fd5b503461080f57602060031936011261080f5760406020916001600160a01b036136b36141e6565b168152600f83522054604051908152f35b503461080f576003196060813601126108075767ffffffffffffffff6004351161080757610120906004353603011261080f5760243567ffffffffffffffff8111610807576137179036906004016142d9565b6101205260443567ffffffffffffffff81116108075761373b90369060040161423c565b60e052613746614820565b50819061375c6101205160246004350135615674565b61376e610120516004356004016156d8565b6001600160a01b036137846044600435016147d2565b16331460c052826080528283608052839261379d614774565b610100528490602061010051015180151580613e8b575b613e72575b506001949294918660a0525b6137d9608460043501600435600401614c65565b905060a051101561391d5760a06137f8815160e06101205101516145a5565b519161385d61381c613814608460043501600435600401614c65565b855191614764565b3561382b6044600435016147d2565b906001600160a01b0360606101205101511691868661012051015193610417610100519560c061012051015190614799565b9394929991959096015160035460c0519160c051613913575b826138c2575b50506104705761389f61045e946138996138b0986138a894614799565b9c614799565b98608051614799565b608052614799565b60a080516001019052929492916137c5565b9091506138d560c460043501358261497c565b612710907e068db8bac710cb295e9e1b089a027525460aa64c2f837b4a2339c0ebedfa43811182021583021561390f57020410388061387c565b8f80fd5b8615159250613876565b8692938561394b88936001600160a01b036060610120510151169061010051916105116044600435016147d2565b60016080519611613e485760c05115613c83575060a4600435013590613978426101046004350135614799565b6139a361399460c061012051015160a061012051015190614799565b9260806101205101519061549a565b6139ab614740565b856139b6818761497c565b15159586613c0c575b505050508215613bfc575b8215613bf2575b5050613bc8575b6139ee6064600435013560a4600435013561497c565b91613bb2575b81811115613b605790613a39916001600160a01b03606061012051015116916120d76001600160a01b0361012051511691613a336044600435016147d2565b9361497c565b6001600c54019182600c55613a6083613a50614940565b9261057d366004356004016153aa565b613a6982614598565b52613a7381614598565b5060e0610120510152613a9e613a9060a06101205101514261497c565b610104600435013590614799565b60c061012051015260a460043501356080610120510152613ac161012051615d37565b8282526014602052604082205560246004350135815260408120557f9190f728135a4ce2ee0c985cc2c2ce55169f358d6a979e6d48add4743482621c60405160043560040135815260246004350135602082015282604082015260a0606082015280613b3360a08201610120516143c9565b6064600435013560808301520390a160405190815260406020820152806106e060408201610120516143c9565b818110613b6f575b5050613a39565b613bab916001600160a01b03606061012051015116916120d7613b966044600435016147d2565b916001600160a01b036101205151169361497c565b8280613b68565b9250613bc1906080519061497c565b81926139f4565b60046040517fc9031bb4000000000000000000000000000000000000000000000000000000008152fd5b10905085806139d1565b60043560640135151592506139ca565b83949650613c3890613c32613c2584613c3e96976154ba565b9160c460043501356154ba565b9061497c565b936154ba565b907e068db8bac710cb295e9e1b089a027525460aa64c2f837b4a2339c0ebedfa43831161271002158202156115865760206127109101519202041091878085816139bf565b9250506001600160a01b03610120515116331415600014613cc85760046040517fd1988c2d000000000000000000000000000000000000000000000000000000008152fd5b600191613cd96044600435016147d2565b90613ce9366004356004016153aa565b9160608686805b613de5575b50508351936020810151916001600160a01b03604083015116906060830151906020815191012060a08401519160c08501519361010060e0870151960151966040519a7fdb613ea3383336cd787d929ccfc21ab7cd87bf1d588780c80ce5f970dd79c34860208d015260408c015260608b015260808a015260a089015260c088015260e0870152610100860152610120850152610140908185015283528261016081011067ffffffffffffffff61016085011117613dd157908291610160613dcc940160405260e051926020815191012090615095565b6139d8565b602486634e487b7160e01b81526041600452fd5b909160808601518051841015613e3f5790613e366040613e068686956145a5565b519281519381613e2086935180926020808701910161419e565b820190602082015203602081018452018261417b565b92019080613cf0565b50915080613cf5565b60046040517fbf85e051000000000000000000000000000000000000000000000000000000008152fd5b613e84919250606460043501356149e6565b90386137b9565b506064600435013515156137b4565b503461080f57602060031936011261080f577f47487988ae8c1417174bb9698afe3a54ef641a5255053a53eabc4d188fe6875d6020613ed76141e6565b6001600160a01b0390613eef826001541633146144fe565b168073ffffffffffffffffffffffffffffffffffffffff196017541617601755604051908152a180f35b503461080f57602060031936011261080f5760406020916001600160a01b03613f406141e6565b168152601183522054604051908152f35b503461080f57608060031936011261080f57613f6b6141e6565b50613f746141fc565b5060643567ffffffffffffffff811161080757613f9590369060040161423c565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b823461080f578060031936011261080f576005548282613fe0836140a0565b9283835260209460019186838216918260001461407d575050600114614023575b505061400f9250038361417b565b6106e06040519282849384528301906141c1565b90859250600582527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db05b85831061406557505061400f93508201018580614001565b8054838901850152879450869390920191810161404d565b9150935061400f95925060ff1991501682840152151560051b8201018580614001565b90600182811c921680156140d0575b60208310146140ba57565b634e487b7160e01b600052602260045260246000fd5b91607f16916140af565b6060810190811067ffffffffffffffff8211176124de57604052565b67ffffffffffffffff81116124de57604052565b60c0810190811067ffffffffffffffff8211176124de57604052565b610100810190811067ffffffffffffffff8211176124de57604052565b6040810190811067ffffffffffffffff8211176124de57604052565b60e0810190811067ffffffffffffffff8211176124de57604052565b90601f601f19910116810190811067ffffffffffffffff8211176124de57604052565b60005b8381106141b15750506000910152565b81810151838201526020016141a1565b90601f19601f6020936141df8151809281875287808801910161419e565b0116010190565b600435906001600160a01b03821682036104e757565b602435906001600160a01b03821682036104e757565b604435906001600160a01b03821682036104e757565b35906001600160a01b03821682036104e757565b9181601f840112156104e75782359167ffffffffffffffff83116104e757602083818601950101116104e757565b67ffffffffffffffff81116124de5760051b60200190565b91908260c09103126104e75760405161429a8161410a565b60a0808294803584526142af60208201614228565b60208501526040810135604085015260608101356060850152608081013560808501520135910152565b9190610100838203126104e7576040516142f281614126565b80936142fd81614228565b82526020808201358184015261431560408301614228565b604084015261432660608301614228565b60608401526080820135608084015260a082013560a084015260c091828101358385015260e08101359067ffffffffffffffff82116104e757019084601f830112156104e75781356143778161426a565b95614385604051978861417b565b81875282858189019302850101938185116104e7578301915b8483106143b15750505050505060e00152565b8386916143be8486614282565b81520192019161439e565b610100918281016001600160a01b03918284511681526020908185015182820152604094848682015116868301526060918583830151168382015260809586830151878301528461012060a093848601518582015260e060c096878101518884015201519b60e08201528b51809952019901966000945b878610614454575050505050505050505090565b885180518c528088015184168c890152808b01518c8c0152808201518c830152808301518c8401528401518b8501529984019997860197600190950194614440565b9181601f840112156104e75782359167ffffffffffffffff83116104e7576020808501948460051b0101116104e757565b906003196040818401126104e757600435926024359167ffffffffffffffff83116104e75782610100920301126104e75760040190565b1561450557565b606460405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152fd5b903590601e19813603018212156104e7570180359067ffffffffffffffff82116104e7576020019181360383136104e757565b67ffffffffffffffff81116124de57601f01601f191660200190565b80511561340f5760200190565b805182101561340f5760209160051b010190565b6000467f00000000000000000000000000000000000000000000000000000000000000000361460757507f000000000000000000000000000000000000000000000000000000000000000090565b6040516005549190818161461a856140a0565b918282526020958683019560019188838216918260001461471f5750506001146146c5575b505061464d9250038261417b565b51902090604051908101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260408201527fad7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a560608201524660808201523060a082015260a081526146bf8161410a565b51902090565b90879250600582527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db05b85831061470757505061464d9350820101388061463f565b805483880185015286945088939092019181016146ef565b9150935061464d95925060ff199150168652151560051b820101388061463f565b6040519061474d826140da565b600254825260035460208301526004546040830152565b919081101561340f5760051b0190565b6040519061478182614143565b6007546001600160a01b031682526008546020830152565b919082018092116147a657565b634e487b7160e01b600052601160045260246000fd5b6004356001600160a01b03811681036104e75790565b356001600160a01b03811681036104e75790565b6001600160a01b0316156147f657565b60046040517f92863bea000000000000000000000000000000000000000000000000000000008152fd5b6040519061482d82614126565b606060e083600080825280602083015280604083015280848301528060808301528060a083015260c08201520152565b1561486457565b606460405162461bcd60e51b815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61813603018212156104e7570190565b9035907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe61813603018212156104e7570190565b6040519061491b8261410a565b8160a06000918281528260208201528260408201528260608201528260808201520152565b6040519061494d82614143565b600182528160005b602090818110156149775760209161496b61490e565b90828501015201614955565b505050565b919082039182116147a657565b9291926149958261457c565b916149a3604051938461417b565b8294818452818301116104e7578281602093846000960137010152565b9064496cebb80091816000190481118202158302156104e7570290808204910615150190565b9061271091816000190481118202158302156104e7570290808204910615150190565b9160008093602095606494604051946323b872dd60e01b86526001600160a01b03809216600487015216602485015260448401525af13d15601f3d1160016000511416171615614a5557565b606460405162461bcd60e51b815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b9080601f830112156104e757816020614ab493359101614989565b90565b91906040838203126104e75760405190614ad082614143565b8193614adb81614228565b835260208101359167ffffffffffffffff83116104e757602092614aff9201614a99565b910152565b9190916101a09081818503126104e75760405167ffffffffffffffff928101838111828210176124de57604052809482358252602090614b45828501614228565b8284015260408401356040840152614b5f60608501614228565b606084015260808401356080840152614b7a60a08501614228565b60a084015260c084013560c0840152614b9560e08501614228565b60e084015261010080850135908401526101208085013590840152610140808501359084015261016080850135908401526101809384810135908682116104e7570181601f820112156104e7578035614bed8161426a565b96614bfb604051988961417b565b818852848089019260051b840101938085116104e757858401925b858410614c2857505050505050500152565b83358381116104e7578791614c42848480948a0101614ab7565b815201930192614c16565b908160209103126104e7575180151581036104e75790565b903590601e19813603018212156104e7570180359067ffffffffffffffff82116104e757602001918160051b360383136104e757565b601f8260209493601f19938186528686013760008582860101520116010190565b60c0810135908115614cfa575003614cd057565b60046040517fe69ab6be000000000000000000000000000000000000000000000000000000008152fd5b9190610180830191614d0c8385614c65565b8091501580615054575b15614d455760046040517fe69ab6be000000000000000000000000000000000000000000000000000000008152fd5b6001811480615019575b6150125791909493926000908035915b848110614d70575050505050509050565b614d7a8287614c65565b82101561340f5760059082821b810135603e199182813603018212156104e757614da691369101614ab7565b906001600160a01b03808351169160208094015190833b156104e75789958c601e1996604097889283519a7fdbe33d66000000000000000000000000000000000000000000000000000000008c5288614e308d60609081600482015260a46102048201998735606484015285614e1d8b8a01614228565b1660848401528701359101528401614228565b1660c48d0152608082013560e48d015288614e4d60a08401614228565b166101048d01526101248c015287614e6760e08301614228565b166101448c01526101008101356101648c01526101208101356101848c01526101408101356101a48c01526101608101356101c48c01523603018c12156104e7578e8c018035928282019767ffffffffffffffff958686116104e75785901b918236038a136104e757948094939296956101a06101e48f9a949a015252610224808d01968d01019896600091605e1984360301935b868410614f72575050505050505050505091846000818f9b9c9d9e9f81958397614f369260248501526003198483030160448501526141c1565b03925af1908115614f6857509060019291614f59575b5001949392969594614d5f565b614f62906140f6565b38614f4c565b513d6000823e3d90fd5b9091929394959697989a7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffddc8f8203018a528b35868112156104e75782018e85614fbc8a8401614228565b1683528101359084813603018212156104e7570187810135908f018a82116104e75781360381136104e7578f89929183928286615000948660019901520191614c9b565b9d019a01989796959401929190614efc565b5050505050565b506150248486614c65565b1561340f57803590603e19813603018212156104e7576001600160a01b039161504d91016147d2565b1615614d4f565b50811515614d16565b908160209103126104e757517fffffffff00000000000000000000000000000000000000000000000000000000811681036104e75790565b9291909260426150a36145b9565b6040958651917f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201522092813b15156000146151d85791602091615135936001600160a01b0387518096819582947f1626ba7e000000000000000000000000000000000000000000000000000000009a8b855260048501528b60248501526044840191614c9b565b0392165afa9081156151cd57907fffffffff000000000000000000000000000000000000000000000000000000009160009161519f575b5016036151765750565b600490517fd9681a86000000000000000000000000000000000000000000000000000000008152fd5b6151c0915060203d81116151c6575b6151b8818361417b565b81019061505d565b3861516c565b503d6151ae565b83513d6000823e3d90fd5b926151ec6151fb936151f293953691614989565b90615210565b909291926152dc565b6001600160a01b038091169116036151765750565b81519190604183036152415761523a92506020820151906060604084015193015160001a9061524c565b9192909190565b505060009160029190565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084116152d057926020929160ff608095604051948552168484015260408301526060820152600092839182805260015afa156152c45780516001600160a01b038116156152bb57918190565b50809160019190565b604051903d90823e3d90fd5b50505060009160039190565b600481101561539457806152ee575050565b600181036153205760046040517ff645eedf000000000000000000000000000000000000000000000000000000008152fd5b6002810361535957602482604051907ffce698f70000000000000000000000000000000000000000000000000000000082526004820152fd5b6003146153635750565b602490604051907fd78bce0c0000000000000000000000000000000000000000000000000000000082526004820152fd5b634e487b7160e01b600052602160045260246000fd5b9190916101209081818503126104e7576040519182019367ffffffffffffffff94838110868211176124de5760405282948235845260209081840135828601526153f660408501614228565b60408601526060840135606086015260808401359081116104e75783019180601f840112156104e757823561542a8161426a565b93615438604051958661417b565b818552838086019260051b8201019283116104e7578301905b82821061548b5750505050608083015260a081013560a083015260c081013560c083015260e081013560e083015261010080910135910152565b81358152908301908301615451565b81156154a4570490565b634e487b7160e01b600052601260045260246000fd5b818102929181159184041417156147a657565b903590605e19813603018212156104e7570190565b903590601e19813603018212156104e7570180359067ffffffffffffffff82116104e7576020019160c08202360383136104e757565b908160209103126104e757516001600160a01b03811681036104e75790565b939590949194600094600097889461555c60808a0197610e57610e4e8a51809361497c565b428111610f2257506040890199615574838c5161497c565b9a80519861558860a08d019a8b51906154ba565b998d1561565e578d61332360209361331d6155a6945191514261497c565b9301518061564b575b5060608b019182519151908d80600019048411021582021561080f5750908c95949392918682020493615617575b505061560e87610e496155f861561598979695602095614799565b809d6001600160a01b0395869101511696614799565b9316614a09565b565b6020929a50610e496155f88561560e946156386156159b9a99988e9661497c565b905260019d9550959697985050506155dd565b615657919850836149e6565b96386155af565b50919b508b9a50600199508a9750505050505050565b61567d82615d37565b81600052601460205260406000205403610d7457508060c060a06156a693015191015190614799565b42116156ae57565b60046040517f6d4241c0000000000000000000000000000000000000000000000000000000008152fd5b9060a0820135159081156157ce575b506104705760e081013580421161579d57506157076040823592016147d2565b6001600160a01b03811682600082815260106020526040812082825260205260ff604082205416928315615785575b505050615741575050565b6040517f0d7043a00000000000000000000000000000000000000000000000000000000081526001600160a01b039190911660048201526024810191909152604490fd5b60409293508152601160205220541015823880615736565b602490604051907f2f06d7780000000000000000000000000000000000000000000000000000000082526004820152fd5b60e091500151516157e26080830183614c65565b9190501415386156e7565b906157f661490e565b506001600160a01b036040830151169160c060a0820151910151926040519461581e8661410a565b855260208501526040840152606083015242608083015260a082015290565b601554907f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821682036147a657614ab49160011b9061549a565b9060208401511515806158b0575b615890575b50505050565b6158a7936001600160a01b03809151169216614a09565b3880808061588a565b50821515615885565b6024356fffffffffffffffffffffffffffffffff811681036104e75790565b6044356fffffffffffffffffffffffffffffffff811681036104e75790565b815115615a3057600092338452601260205260ff6040852054161592831561596d575b5050506159435760406fffffffffffffffffffffffffffffffff91338152601360205220541690565b60046040517f4eba9946000000000000000000000000000000000000000000000000000000008152fd5b6159cf9293509060209160405193849283926159b87f628b220700000000000000000000000000000000000000000000000000000000988986526060600487015260648601906143c9565b9160248501526003198483030160448501526141c1565b038186335af19081156129b357907fffffffff00000000000000000000000000000000000000000000000000000000918491615a12575b5016141538808061591a565b615a2a915060203d81116151c6576151b8818361417b565b38615a06565b505050600090565b9091615a45368285614989565b5115615a3057600091338352601260205260409360ff858520541615928315615aa9575b505050615a80573381526013602052205460801c90565b600482517f4eba9946000000000000000000000000000000000000000000000000000000008152fd5b615b049293509060209186519384928392615af27f94a2d3d500000000000000000000000000000000000000000000000000000000988986528b600487015260448601906143c9565b91600319858403016024860152614c9b565b038186335af1908115615b6557907fffffffff00000000000000000000000000000000000000000000000000000000918491615b47575b50161415388080615a69565b615b5f915060203d81116151c6576151b8818361417b565b38615b3b565b84513d85823e3d90fd5b6000919060605b6101808201518051851015615c385784615b8f916145a5565b516001600160a01b0381511660208092015182815191012091604090815193818501937f4def3e04bd42194484d5f8a5b268ec0df03b9d9d0402606fe3100023c5d79ac4855283860152606085015260608452608084019380851067ffffffffffffffff8611176124de57600195615c30948685528251902060a091615c1d8151809287868801910161419e565b830191820152039081018452018261417b565b930192615b76565b509192508051916001600160a01b0391826020820151169260408201519080606084015116906080840151908060a0860151169160c08601519160e08701511692610100918288015195610120890151976101606101408b01519a01519a602081519101209b60606040519e8f907f891e530ed2768a9decac48f4b7beec447f755ce23feeeeb952e429145b44ba9160208301526040820152015260808d015260a08c015260c08b015260e08a01528801526101208701526101408601526101608501526101808401526101a08301526101c0908183015281526101e0810181811067ffffffffffffffff8211176124de576040526020815191012090565b90600060605b60e08401518051831015615e095790615e01615d5b846001946145a5565b5160206001600160a01b03818301511660409283810151906060810151608091828101519160a08092015193885195888701977f8ca047c2f10359bf4a27bd2c623674be3801153b6b2646ba08593dc96ad7bb4489528a880152606087015285015283015260c090818301528152615dd28161415f565b5190209382519481615ded879351809286808701910161419e565b82019083820152039081018452018261417b565b910190615d3d565b50919290506001600160a01b038082511692602083015192826040820151169260608201511660808201519060c060a08401519301519360208151910120946040519660208801987f35f73c5cb07b3fa605378d4f576769166fed212ec3813ac1f1d73ef1c537eb0e8a5260408901526060880152608087015260a086015260c085015260e084015261010083015261012090818301528152610140810181811067ffffffffffffffff8211176124de576040525190209056fea2646970667358221220eba2315c8271639a56006b67768be0c0c6e366fb6841900f2b4b05edcbe1d25864736f6c63430008150033036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db000000000000000000000000097d34635b605c2f1630d6b4c6c5d222b8a2ca47d000000000000000000000000d07a25e6a22e9158162699490aa6a9464e14c50a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000004150ded32a6d3bfecae76e7558af48019034492700000000000000000000000052ac424ef7b283aa5badb8c6254832e3280d7398000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000447e69651d841bd8d104bed4930000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x61014080604052600436101561001457600080fd5b600090813560e01c90816306fdde0314613fc157508063150b7a0214613f5157806316ec9bc614613f195780631c31cac314613e9a5780632513a5ae146136c45780632891433d1461368c5780632c0a286e146130ef5780633644e515146130d45780633687121b146130ad57806344af681c1461308f5780634ae25a8614612fd05780634ec8198114612f745780634fd930ba14612ef75780635122afb114612e1c5780635448608614612c94578063584d158f14612c6d5780635a32989414612c29578063665458a714612be257806367562c641461285457806367e22404146128195780636ccc9dde146127625780637320ca261461270657806373b99f101461262657806373c2390c146126005780637795960b1461254d5780637b8d1a0b146125235780637d4e41ed14611a5b5780638617780b14611a3d5780638da5cb5b14611a16578063912d819e146119cf578063931c56e9146119b1578063986a7ace14611934578063995fe7201461190d578063a5a41031146118b6578063aa29dad11461188c578063ab250a3a146117cd578063ac9650d814611618578063acb1dfdb146115d6578063b1f0c786146112e7578063b8ca3b83146112ca578063b97e527a14611203578063baab19941461118e578063c2f50a7a14611170578063c833796d14611152578063cc37ef4f14611115578063cf23797d146110f7578063da144a5114611040578063db540a8a14610fc1578063dd7d978214610f7d578063e51e104014610dbd578063e621ffa914610cf4578063e69d8c4714610c8d578063ed21cbd214610ae4578063ed5a42841461095f578063ef706adf146108dc578063f0ad206a1461088d578063f2fde38b146108125763f95cc78f146102a057600080fd5b3461080f576003196040813601126108075767ffffffffffffffff90816004351161080b57610120906004353603011261080757602435908111610807576102ec9036906004016142d9565b6102f4614820565b5060808101610303815161583d565b60a46004350135106107c957506001600160a01b036103266044600435016147d2565b16330361079f5761033d8160246004350135615674565b61034c816004356004016156d8565b61010460043501356107755760646004350135918261074b57600092839081610373614774565b95839260208801519081151580610744575b610731575b5050919290600182935b6103a8608460043501600435600401614c65565b90508510156104ec576103bf8560e08a01516145a5565b5160a061041d8b6103e4896103de608460043501600435600401614c65565b90614764565b358c856103f56044600435016147d2565b916104176001600160a01b036060830151169160c08982015191015190614799565b92615537565b9296919490950151600354841515918261049a575b50506104705760019561045861045e946104526104649861045895614799565b9a614799565b99614799565b93614799565b94019390949291610394565b60046040517f25444587000000000000000000000000000000000000000000000000000000008152fd5b9091506104ad60c460043501358261497c565b612710907e068db8bac710cb295e9e1b089a027525460aa64c2f837b4a2339c0ebedfa4381118202158302156104e7570204103880610432565b600080fd5b508686926105178a886001600160a01b036060860151166105116044600435016147d2565b90615877565b60a4600435013503610470576015548211610700576001600c54019384600c5584601f1961055d6105478661426a565b95610555604051978861417b565b80875261426a565b0160005b8181106106e45750506105829161057d366004356004016153aa565b6157ed565b61058b83614598565b5261059582614598565b50600193835b6105af608460043501600435600401614c65565b9050811015610639576105d0816103de608460043501600435600401614c65565b356105de575b60010161059b565b94600180916105f18860e08701516145a5565b516105fc82886145a5565b5261060781876145a5565b50610620886103de608460043501600435600401614c65565b35604061062d83896145a5565b510152019590506105d6565b507f9190f728135a4ce2ee0c985cc2c2ce55169f358d6a979e6d48add4743482621c848460e085015261066b84615d37565b83825260146020526040822055602460043501358152806040812055604051809160043560040135825260246004350135602083015284604083015260a060608301526106bb60a08301876143c9565b9060808301520390a16106e060405192839283526040602084015260408301906143c9565b0390f35b60209192506106f161490e565b82828801015201908691610561565b602482604051907fc56ee9910000000000000000000000000000000000000000000000000000000082526004820152fd5b61073c9294506149e6565b91388061038a565b5085610385565b60046040517fe791b76b000000000000000000000000000000000000000000000000000000008152fd5b60046040517f32259e57000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3f9b6963000000000000000000000000000000000000000000000000000000008152fd5b60449051604051907f2e67bbe300000000000000000000000000000000000000000000000000000000825260a4600435013560048301526024820152fd5b5080fd5b8280fd5b80fd5b503461080f57602060031936011261080f5773ffffffffffffffffffffffffffffffffffffffff196108426141e6565b600154906001600160a01b039061085c82841633146144fe565b169182911617600155337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b503461080f578060031936011261080f57604080516108ab816140da565b828152826020820152015260606108c0614740565b6040805191805183526020810151602084015201516040820152f35b503461080f57602060031936011261080f577f899cab278284ae4a91172caa0943607a0bcb19766254c3ebe1139be00650b102610959600435338452600e6020526040842081855260205260408420600160ff198254161790556040519182913383602090939291936001600160a01b0360408201951681520152565b0390a180f35b503461080f57606060031936011261080f576109796141e6565b60407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc360112610807576001600160a01b036109ba816001541633146144fe565b6109c3826147e6565b6fffffffffffffffffffffffffffffffff9161271080846109e26158b9565b1611908115610ad1575b50610aa7571690818352601260205260408320600160ff1982541617905560136020526040832081610a1c6158b9565b16815490857fffffffffffffffffffffffffffffffff00000000000000000000000000000000610a4a6158d8565b60801b169216171790556040519182526024358181168091036104e75760208301526044359081168091036104e7578160609160407f58b1f18b665c4c540aef11f1c3dec18c43824ab0e2e8057a7a9bc6ad3653be24940152a180f35b60046040517fb53b7b15000000000000000000000000000000000000000000000000000000008152fd5b905083610adc6158d8565b1611386109ec565b503461080f5760031960a081360112610807576004356024359167ffffffffffffffff8311610c895761010083600401918436030112610c8957610b26614212565b90608435918215158093036104e757610b47610b4236846142d9565b615d37565b9184875260209260148452604088205403610c715790610b71916001600160a01b039283916147d2565b163303610c47578160a484928983601754166024610b9160448d016147d2565b9b604051988997889663b18e2bbb60e01b8852169d8e600488015216828601520135604484015260643560648401528860848401525af18015610c3c57610c0d575b50907f696a99e04dd3d11d9ec4fe68917db2c142d3a55e1bff074c34ad74c1d515547d93606093926040519384528301526040820152a180f35b9080939291813d8311610c35575b610c25818361417b565b810103126104e757909138610bd3565b503d610c1b565b6040513d88823e3d90fd5b60046040517fdbd91fb4000000000000000000000000000000000000000000000000000000008152fd5b6024856040519063047e261360e21b82526004820152fd5b8380fd5b503461080f578060031936011261080f5760006020604051610cae81614143565b82815201526106e0604051610cc281614143565b6009546001600160a01b0316808252600a54602092830190815260408051928352905192820192909252918291820190565b503461080f57610d03366144c7565b6001600160a01b0360065460301c16803303610d8c5750610b42610d289136906142d9565b8183526014602052604083205403610d74577faf91ffe368225045d42f7c2b4b90feb438133ac1e375e73bc9066652b70a1d776020604051838152a18152601460205280604081205580f35b6024906040519063047e261360e21b82526004820152fd5b602490604051907f8afaa9b50000000000000000000000000000000000000000000000000000000082526004820152fd5b503461080f57606060031936011261080f576004359060243567ffffffffffffffff811161080757610df39036906004016142d9565b91604435610dff614820565b50610e0a8483615674565b60e0840191600183515111610f53576080610e258451614598565b51015192610e5d60a087015194610e57610e4e82610e4960c08c01998a5190614799565b61497c565b601654906149e6565b90614799565b428111610f2257506020610e796001600160a01b039251614598565b51015116330361079f5782610eb0837f611e850fd4909c086538320a1f747a380fdaff020cc4e6649b9b4dd0dbd383219551614799565b90526001600c54019384600c55610ec686615d37565b858252601460205260408220558181526040812055604051918291825284602083015260806040830152610efd60808301876143c9565b9060608301520390a16106e060405192839283526040602084015260408301906143c9565b602490604051907f4d321dfb0000000000000000000000000000000000000000000000000000000082526004820152fd5b60046040517fcd23b291000000000000000000000000000000000000000000000000000000008152fd5b503461080f578060031936011261080f5760206040516001600160a01b037f00000000000000000000000052ac424ef7b283aa5badb8c6254832e3280d7398168152f35b503461080f57602060031936011261080f577faed358a2bd4ca37fb72362cad5ae66b0b49a2a2031f9474a656e47b1f4afc4196020610ffe6141e6565b6001600160a01b0390611016826001541633146144fe565b168073ffffffffffffffffffffffffffffffffffffffff196018541617601855604051908152a180f35b503461080f57602060031936011261080f5760043533825260116020526040822054818110156110b55750338083526011602090815260408085208490558051928352908201929092527f5009c2e03e218242def2d459330d4f8b8b7eb7289ca08c47489aeef06672c63c9181908101610959565b6040517f997e86d600000000000000000000000000000000000000000000000000000000815233600482015260248101929092526044820152606490fd5b0390fd5b503461080f578060031936011261080f576020601654604051908152f35b503461080f57602060031936011261080f5760ff60406020926001600160a01b0361113e6141e6565b168152601284522054166040519015158152f35b503461080f578060031936011261080f576020601554604051908152f35b503461080f578060031936011261080f576020604051620151808152f35b503461080f57606060031936011261080f576111b66001600160a01b036001541633146144fe565b7f36e7ba2b68a5f2a92d31c35c23faa8f6fb2559d4406bb30eef08bdbab0d6f10960606004358060025560243580600355604435908160045560405192835260208301526040820152a180f35b503461080f57602060031936011261080f5760043565ffffffffffff81168091036108075761123e6001600160a01b036001541633146144fe565b6201518081106112a0576020817f9b0306e96c09148e30f9acd9a1ebc2c7cb1bc0348adbf320530ead875f78292c927fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000006006541617600655604051908152a180f35b60046040517fa922f92c000000000000000000000000000000000000000000000000000000008152fd5b503461080f578060031936011261080f5760206040516109c48152f35b503461080f576003196080813601126108075767ffffffffffffffff6004356024358281116115d257610100816004019482360301126115d257611329614212565b926064359081116115ce5761134290369060040161423c565b919094611352610b4236836142d9565b84885260146020526040882054036115b657611378906001600160a01b039182916147d2565b163303610c475760448201878261138e836147d2565b16936024846018541691013594803b1561080b576040516323b872dd60e01b81523060048201526001600160a01b039290921660248301526044820186905282908290606490829084905af180156115ab57611597575b50508160185416886113f6836147d2565b823b1561080757899885938484809461145b604051988996879586947fee534a3f0000000000000000000000000000000000000000000000000000000086521660048501528d6024850152169e8f6044840152608060648401528d6084840191614c9b565b03925af191821561158a57849261156f575b505061147a6020926147d2565b16926024604051809581937f6352211e00000000000000000000000000000000000000000000000000000000835260048301525afa918215611564578792611534575b503091160361150a577f13ab7606a034e1d4320c1129442ffa929fe2789f8cd0d03d147ff4f4707567a5936109599160405194859485526020850152606060408501526060840191614c9b565b60046040517f807a2b8f000000000000000000000000000000000000000000000000000000008152fd5b61155691925060203d811161155d575b61154e818361417b565b810190615518565b90386114bd565b503d611544565b6040513d89823e3d90fd5b61157a9192506140f6565b6115865781883861146d565b8780fd5b50604051903d90823e3d90fd5b6115a0906140f6565b6115865787386113e5565b6040513d84823e3d90fd5b6024846040519063047e261360e21b82526004820152fd5b8580fd5b8480fd5b503461080f57604060031936011261080f5760406020916001600160a01b036115fd6141e6565b168152600d8352818120602435825283522054604051908152f35b506020806003193601126108075760043567ffffffffffffffff811161080b5761164783913690600401614496565b6116508161426a565b9161165e604051938461417b565b818352601f1961166d8361426a565b01845b8181106117be575050835b8281106116ff5750505060405191838301848452825180915260408401948060408360051b870101940192955b8287106116b55785850386f35b9091929382806116ef837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08a6001960301865288516141c1565b96019201960195929190926116a8565b84806117138360059998991b850185614549565b9081604051928392833781018381520390305af43d156117b7573d6117378161457c565b90611745604051928361417b565b81523d888883013e5b61175883876145a5565b52156117695760010194939461167b565b61177381856145a5565b51906110f36040519283927f6cf73d1400000000000000000000000000000000000000000000000000000000845260048401526040602484015260448301906141c1565b606061174e565b60608582018801528601611670565b503461080f576020806003193601126108075760043567ffffffffffffffff811161080b57611800903690600401614496565b835b81811061180d578480f35b611818818385614764565b3590338652601085526040862082875285527fa39a039982552bc613a549c5a31d7c9a911b4e57c7da274435fca0e156c9c34061188360408820936001948560ff198254161790556040519182913383602090939291936001600160a01b0360408201951681520152565b0390a101611802565b503461080f57602060031936011261080f5760406020916004358152601483522054604051908152f35b503461080f578060031936011261080f57600060206040516118d781614143565b82815201526106e06118e7614774565b6040519182918291909160208060408301946001600160a01b0381511684520151910152565b503461080f578060031936011261080f5760206001600160a01b0360175416604051908152f35b503461080f57602060031936011261080f577fa39a039982552bc613a549c5a31d7c9a911b4e57c7da274435fca0e156c9c34061095960043533845260106020526040842081855260205260408420600160ff198254161790556040519182913383602090939291936001600160a01b0360408201951681520152565b503461080f578060031936011261080f57602060405162278d008152f35b503461080f57604060031936011261080f5760ff60406020926001600160a01b036119f86141e6565b16815260108452818120602435825284522054166040519015158152f35b503461080f578060031936011261080f5760206001600160a01b0360015416604051908152f35b503461080f578060031936011261080f576020600c54604051908152f35b503461080f576003199060208236011261080f576004359167ffffffffffffffff83116108075760a090833603011261080f57611a96614820565b50611aa4600182541461485d565b60028155611ab4602483016147d2565b611ac0604484016147d2565b91611ad7611ad160048601806148a8565b806148db565b936001600160a01b03611aec602087016147d2565b1661251c57835b611b0060048301806148a8565b94611b116064840184600401614549565b9690611b236084860186600401614549565b611b386020611b3286806148db565b016147d2565b91611b486060611b3287806148db565b93611b5386806148db565b9b8c916001600160a01b038d1633036124f8575b5050506001600160a01b03871633036123b9575b50506101408901358042116123885750606083013580421161238857506001600160a01b0384168752600e602052604087208935885260205260ff6040882054168015612368575b61232757610100890135806040850135116122ed5750611be560e08a016147d2565b6001600160a01b03604051917f3af32abf0000000000000000000000000000000000000000000000000000000083521660048201526020816024816001600160a01b037f0000000000000000000000004150ded32a6d3bfecae76e7558af480190344927165afa9081156122995788916122ce575b50156122a457611c6c60a08a016147d2565b6001600160a01b03604051917f3af32abf0000000000000000000000000000000000000000000000000000000083521660048201526020816024816001600160a01b037f00000000000000000000000052ac424ef7b283aa5badb8c6254832e3280d7398165afa90811561229957889161226a575b5015612240576001600160a01b0316801515908161222c575b50612202576001600160a01b031680151590816121ee575b506121c4576101608701351561219a57610120870135156121705760808701358015159081612139575b5061210f5760209687611d5192013590614cbc565b6001600c54019485600c556040611d6e85600401866004016148a8565b013593611d79614940565b604051611d858161410a565b8881526001600160a01b0384168a8201528660408201528760608201524260808201526101208b013560a0820152611dbc82614598565b52611dc681614598565b506001600160a01b0389611ddd60048501806148a8565b0135818c611df960e0611df260a084016147d2565b92016147d2565b926040519d8e611e0881614126565b848c16815201521660408c01521660608a01528560808a01524260a08a01526101608a013560c08a015260e0890152611e4088615d37565b87875260146020526040872055610100890135856000190460408b0135118602158102156104e757899086604083013502908082049106151501611e82614774565b611e8e60e084016147d2565b91602082019282878551611ea290856149e6565b90611eac93615877565b611eb860e085016147d2565b6001600160a01b03168689611ecd848d61497c565b91611ed793614a09565b611ee460048601806148a8565b60808101611ef191614549565b3690611efc92614989565b90611f07918d6158f7565b6fffffffffffffffffffffffffffffffff168061209d575b505050611f466001600160a01b03611f3b60a0602094016147d2565b1692600401806148a8565b0135813b15612099576040516323b872dd60e01b81526001600160a01b038616600482015230602482015260448101919091529086908290606490829084905af18015610c3c57612085575b50877f2b81fc2f914e99b38053262ace9e49b2f47f712ef594be1ea005de4f3baeec9d88886080946001600160a01b0360019b9c9d359881611fec60405196879687528c602088015260c0604088015260c08701906143c9565b931660608501521686830152604085013560a08301520390a1013515612055576001600160a01b03168352600d6020526040832090835260205261203560408320918254614799565b90555b556106e060405192839283526040602084015260408301906143c9565b6001600160a01b03919250168252600e60205260408220908252602052604081208260ff19825416179055612038565b61208f86916140f6565b6115d25738611f92565b8680fd5b6120b56120ad6120dd928b6149e6565b9351846149e6565b9286896120d7866001600160a01b036120d060e08b016147d2565b169461497c565b92614a09565b868215611f1f576001600160a01b03806120fc60e061210697016147d2565b1692511691614a09565b88388086611f1f565b60046040517fdab69d5e000000000000000000000000000000000000000000000000000000008152fd5b90506001600160a01b0383168652600d602052604086208835875260205261216960408301356040882054614799565b1138611d3c565b60046040517f20dac71b000000000000000000000000000000000000000000000000000000008152fd5b60046040517fecd83f88000000000000000000000000000000000000000000000000000000008152fd5b60046040517f1548268b000000000000000000000000000000000000000000000000000000008152fd5b90506001600160a01b038416141538611d12565b60046040517f62257e48000000000000000000000000000000000000000000000000000000008152fd5b90506001600160a01b038816141538611cfa565b60046040517ff1a638e6000000000000000000000000000000000000000000000000000000008152fd5b61228c915060203d602011612292575b612284818361417b565b810190614c4d565b38611ce1565b503d61227a565b6040513d8a823e3d90fd5b60046040517fa212b5d3000000000000000000000000000000000000000000000000000000008152fd5b6122e7915060203d60201161229257612284818361417b565b38611c5a565b8360449160408051927f111c5f29000000000000000000000000000000000000000000000000000000008452013560048301526024820152fd5b6040517ffc4a5c250000000000000000000000000000000000000000000000000000000081526001600160a01b038516600482015289356024820152604490fd5b506001600160a01b0384168752600f602052604087205489351115611bc3565b602490604051907f5929d5bb0000000000000000000000000000000000000000000000000000000082526004820152fd5b60a0853603126124f4576040518060a081011067ffffffffffffffff60a0830111176124de5760a0810160405267ffffffffffffffff8635116124da576124033687358801614b04565b815260208601356020820152604086013560408201526060860135606082015267ffffffffffffffff6080870135116124da57906124d3929161244c3660808901358901614a99565b6080820190815261245d8251615b6f565b9160208101519160606040830151920151905160208151910120916040519360208501957f7e90717662b6dd110797922ef6d6701d92bfd4164783966933e092ea21a74c5a875260408601526060850152608084015260a083015260c082015260c081526124ca8161415f565b51902088615095565b3880611b7b565b8980fd5b634e487b7160e01b600052604160045260246000fd5b8880fd5b61250e612509612514943690614b04565b615b6f565b86615095565b8a3880611b67565b8293611af3565b503461080f578060031936011261080f5760206001600160a01b0360065460301c16604051908152f35b503461080f57602060031936011261080f57600435338252600f6020526040822054818110156125c2575033808352600f602090815260408085208490558051928352908201929092527feaacd06746bfd850a83bbe929f94f4e1413f9aac82dc76e4331418aaf8bba4689181908101610959565b6040517f570f5b7800000000000000000000000000000000000000000000000000000000815233600482015260248101929092526044820152606490fd5b503461080f578060031936011261080f57602065ffffffffffff60065416604051908152f35b503461080f578060031936011261080f576001600160a01b0361264e816001541633146144fe565b600b5462278d0081018082116126f25742106126c1575060407fb3c1d38dbdc9199d0ce01f386d70e29014ed4af7af1d321ca6641a91f4b4dc0c91600954168073ffffffffffffffffffffffffffffffffffffffff196007541617600755600a548060085582519182526020820152a180f35b602490604051907f5c269db50000000000000000000000000000000000000000000000000000000082526004820152fd5b602484634e487b7160e01b81526011600452fd5b503461080f57602060031936011261080f577fe818ff6972bf8970ca14a893539e57452588b197b76e6afe29c40a31b3363c6160206004356127546001600160a01b036001541633146144fe565b80601655604051908152a180f35b503461080f57602060031936011261080f576004356001600160a01b03808216808303610c89577f0656dc0fa3e59bca53331c14b87a4778ffac8c184915152fdd0f103838a4f378926127bc6020936001541633146144fe565b6127c5826147e6565b7fffffffffffff0000000000000000000000000000000000000000ffffffffffff79ffffffffffffffffffffffffffffffffffffffff0000000000006006549260301b16911617600655604051908152a180f35b503461080f578060031936011261080f5760206040517f24fc88ca4cc577be38175f01dd694014086415b93c7410ba2b863edc7b9e87648152f35b503461080f57612863366144c7565b91612871600182541461485d565b60028155612882610b4236856142d9565b9180825260209260148452604083205403610d74576128a960c085013560a0860135614799565b936001600160a01b0394856128c0604084016147d2565b1690428111612bb1575060609560e083019060016128de83866154e2565b9050146000146129d257506128f390836154e2565b156129be578561290391016147d2565b91813b156115d2576040516323b872dd60e01b81523060048201526001600160a01b03939093166024840152850135604483015283908290606490829084905af180156129b35761299f575b50906001917f5985c1ea46d5ed2be4837f88c0563e18ed022c3bfe10dee15f6ce74daf897d6384604051838152a18152601483528060408120555b556106e06040519282849384528301906141c1565b6129a983916140f6565b610807573861294f565b6040513d85823e3d90fd5b602485634e487b7160e01b81526032600452fd5b96919050600654918684013592823b15612099576040516323b872dd60e01b815230600482015260309190911c89166001600160a01b0316602482015260448101849052868160648183875af180156115645791899188959493612b94575b5060c49291612a45869260065498016147d2565b968160405198899788967f762d7d740000000000000000000000000000000000000000000000000000000088528c60048901526024880152604487015216606485015265ffffffffffff811660848501523360a485015260301c165af19081156129b3578391612af0575b506040600193927f0c0f58e3bcc7b4b6f90dac7e83ad948f2dfe00aff9393c51aa2181fa08fb94e1929660065460301c16825191825286820152a161298a565b90503d8084833e612b01818361417b565b8101908481830312610c895780519067ffffffffffffffff82116115d2570181601f82011215610c8957805190612b378261457c565b92612b45604051948561417b565b8284528683830101116115d2579282612b8a6040936001979689807f0c0f58e3bcc7b4b6f90dac7e83ad948f2dfe00aff9393c51aa2181fa08fb94e19801910161419e565b9250929350612ab0565b612ba29193949592506140f6565b6115ce57908786939238612a31565b602490604051907fb73a6a130000000000000000000000000000000000000000000000000000000082526004820152fd5b503461080f57604060031936011261080f5760ff60406020926001600160a01b03612c0b6141e6565b168152600e8452818120602435825284522054166040519015158152f35b503461080f578060031936011261080f5760206040516001600160a01b037f0000000000000000000000004150ded32a6d3bfecae76e7558af480190344927168152f35b503461080f578060031936011261080f5760206001600160a01b0360185416604051908152f35b503461080f57606060031936011261080f57612cae6141e6565b612cb66141fc565b604435916001600160a01b0380831690604051917f6352211e0000000000000000000000000000000000000000000000000000000083528560048401526020928381602481855afa908115612299578891612dff575b5082163014612dd557829060a483601754169389604051958694859363b18e2bbb60e01b85528a16600485015260248401528a6044840152600060648401528160848401525af18015610c3c57612dac575b5050604080516001600160a01b0392831681529290911660208301528101919091527f6ebfd923dee76d83a8a77f3f29fe17755e74e5322d953678e35381cb63709199908060608101610959565b813d8311612dce575b612dbf818361417b565b810103126104e7573880612d5e565b503d612db5565b60046040517f64436547000000000000000000000000000000000000000000000000000000008152fd5b612e169150843d861161155d5761154e818361417b565b38612d0c565b503461080f57604060031936011261080f576001600160a01b03612e45816001541633146144fe565b602435906109c48211612ec6577f160fef22fef07b45037a807beef2c89408a81168d1055cf34024c85396b882af91604091612e87612e826147bc565b6147e6565b612e8f6147bc565b16908173ffffffffffffffffffffffffffffffffffffffff19600954161760095580600a5542600b5582519182526020820152a180f35b602482604051907f8b902c630000000000000000000000000000000000000000000000000000000082526004820152fd5b503461080f57602060031936011261080f577f924773930b2679ab8bf328330b211bedfb8f917551fd856f536bec008d17f9a66020612f346141e6565b6001600160a01b0390612f4c826001541633146144fe565b16808452601282526040842060ff19815416905560138252836040812055604051908152a180f35b503461080f57602060031936011261080f577f759c526639cdac7b56455b1bb335d0edb0ea487d414f1fa706cedf5532f09fc46020600435612fc26001600160a01b036001541633146144fe565b80601555604051908152a180f35b503461080f576020806003193601126108075760043567ffffffffffffffff811161080b57613003903690600401614496565b835b818110613010578480f35b61301b818385614764565b3590338652600e85526040862082875285527f899cab278284ae4a91172caa0943607a0bcb19766254c3ebe1139be00650b10261308660408820936001948560ff198254161790556040519182913383602090939291936001600160a01b0360408201951681520152565b0390a101613005565b503461080f578060031936011261080f576020600b54604051908152f35b503461080f57602060031936011261080f5760206130cc60043561583d565b604051908152f35b503461080f578060031936011261080f5760206130cc6145b9565b503461080f576003199060208236011261080f5767ffffffffffffffff6004351161080f57606060043536039283011261080f57613130600182541461485d565b600281556131426004803501806154cd565b35907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefd60246004350135930183121561080f576001600160a01b0361318c600485813501016147d2565b16330361356a575b6131ad6131a736600486813501016142d9565b83615674565b60406131bd6004803501806154cd565b01358015158103610807576134b9575b6001600160a01b036131e560448560043501016147d2565b166131f5600485813501016147d2565b813b1561080b576040516323b872dd60e01b8152306004808301919091526001600160a01b0392909216602480830191909152913586019091013560448201529082908290606490829084905af180156115ab576134aa575b5061328361326d6132636004803501806154cd565b6020810190614549565b9061327e36600488813501016142d9565b615a38565b9261328c614774565b93602085015115159183906001600160a01b036132af60648360043501016147d2565b16859386945b6132c960048035860160e4810191016154e2565b9050861015613425576132e660048035860160e4810191016154e2565b87101561340f576132fd90369060c0890201614282565b9087613329604084015161332360a086015161331d60808801514261497c565b906154ba565b906149c0565b8c61334a6fffffffffffffffffffffffffffffffff871660408701516149e6565b918c918d946133b5575b508593610e496133ad969461338261338295610e4961338796613382604060019e015160608d015190614799565b614799565b80936001600160a01b0360206133a260048d813501016147d2565b920151169089614a09565b9501946132b5565b6001969450613382925099610e496133ad969461338284610e49613387969f6133f06133e960206133fb93015180956149e6565b93866149e6565b96610e578885614799565b9f965050955050949650509395508e613354565b634e487b7160e01b600052603260045260246000fd5b888486858d8b7fd9cfbcfb9ae5d65b8d34072de4575f602ee25cf3715bc05c291701fe9e258f9a60608f98604051908982526020820152866040820152a161347e575b8585815260146020528060408120556001815580f35b6001600160a01b0361349860046134a196813501016147d2565b91511691614a09565b82808080613468565b6134b3906140f6565b3861324e565b6001600160a01b036017541660206001600160a01b0360a46134e0600488813501016147d2565b856134f160448a60043501016147d2565b9584604051978896879563b18e2bbb60e01b875216600486015216602484015260248a6004350101356044840152816064840152600160848401525af180156115ab5761353f575b506131cd565b602090813d8311613563575b613555818361417b565b8101031261080f5738613539565b503d61354b565b613579600484813501016147d2565b6135876004803501806154cd565b60608136031261080b576040519061359e826140da565b80358252602081013567ffffffffffffffff81116115d2576135c39036908301614a99565b6020830181905291604082013580151590036115d2579060409182820135838201525192602081519101208251937f41277b3c1cbe08ea7bbdd10a13f24dc956f3936bf46526f904c73697d9958e0c6020860152838501526060840152013515156080820152608081528060a081011067ffffffffffffffff60a0830111176136785760a0810160405280516020909101206136739161366b60048035604481019101614549565b929091615095565b613194565b602483634e487b7160e01b81526041600452fd5b503461080f57602060031936011261080f5760406020916001600160a01b036136b36141e6565b168152600f83522054604051908152f35b503461080f576003196060813601126108075767ffffffffffffffff6004351161080757610120906004353603011261080f5760243567ffffffffffffffff8111610807576137179036906004016142d9565b6101205260443567ffffffffffffffff81116108075761373b90369060040161423c565b60e052613746614820565b50819061375c6101205160246004350135615674565b61376e610120516004356004016156d8565b6001600160a01b036137846044600435016147d2565b16331460c052826080528283608052839261379d614774565b610100528490602061010051015180151580613e8b575b613e72575b506001949294918660a0525b6137d9608460043501600435600401614c65565b905060a051101561391d5760a06137f8815160e06101205101516145a5565b519161385d61381c613814608460043501600435600401614c65565b855191614764565b3561382b6044600435016147d2565b906001600160a01b0360606101205101511691868661012051015193610417610100519560c061012051015190614799565b9394929991959096015160035460c0519160c051613913575b826138c2575b50506104705761389f61045e946138996138b0986138a894614799565b9c614799565b98608051614799565b608052614799565b60a080516001019052929492916137c5565b9091506138d560c460043501358261497c565b612710907e068db8bac710cb295e9e1b089a027525460aa64c2f837b4a2339c0ebedfa43811182021583021561390f57020410388061387c565b8f80fd5b8615159250613876565b8692938561394b88936001600160a01b036060610120510151169061010051916105116044600435016147d2565b60016080519611613e485760c05115613c83575060a4600435013590613978426101046004350135614799565b6139a361399460c061012051015160a061012051015190614799565b9260806101205101519061549a565b6139ab614740565b856139b6818761497c565b15159586613c0c575b505050508215613bfc575b8215613bf2575b5050613bc8575b6139ee6064600435013560a4600435013561497c565b91613bb2575b81811115613b605790613a39916001600160a01b03606061012051015116916120d76001600160a01b0361012051511691613a336044600435016147d2565b9361497c565b6001600c54019182600c55613a6083613a50614940565b9261057d366004356004016153aa565b613a6982614598565b52613a7381614598565b5060e0610120510152613a9e613a9060a06101205101514261497c565b610104600435013590614799565b60c061012051015260a460043501356080610120510152613ac161012051615d37565b8282526014602052604082205560246004350135815260408120557f9190f728135a4ce2ee0c985cc2c2ce55169f358d6a979e6d48add4743482621c60405160043560040135815260246004350135602082015282604082015260a0606082015280613b3360a08201610120516143c9565b6064600435013560808301520390a160405190815260406020820152806106e060408201610120516143c9565b818110613b6f575b5050613a39565b613bab916001600160a01b03606061012051015116916120d7613b966044600435016147d2565b916001600160a01b036101205151169361497c565b8280613b68565b9250613bc1906080519061497c565b81926139f4565b60046040517fc9031bb4000000000000000000000000000000000000000000000000000000008152fd5b10905085806139d1565b60043560640135151592506139ca565b83949650613c3890613c32613c2584613c3e96976154ba565b9160c460043501356154ba565b9061497c565b936154ba565b907e068db8bac710cb295e9e1b089a027525460aa64c2f837b4a2339c0ebedfa43831161271002158202156115865760206127109101519202041091878085816139bf565b9250506001600160a01b03610120515116331415600014613cc85760046040517fd1988c2d000000000000000000000000000000000000000000000000000000008152fd5b600191613cd96044600435016147d2565b90613ce9366004356004016153aa565b9160608686805b613de5575b50508351936020810151916001600160a01b03604083015116906060830151906020815191012060a08401519160c08501519361010060e0870151960151966040519a7fdb613ea3383336cd787d929ccfc21ab7cd87bf1d588780c80ce5f970dd79c34860208d015260408c015260608b015260808a015260a089015260c088015260e0870152610100860152610120850152610140908185015283528261016081011067ffffffffffffffff61016085011117613dd157908291610160613dcc940160405260e051926020815191012090615095565b6139d8565b602486634e487b7160e01b81526041600452fd5b909160808601518051841015613e3f5790613e366040613e068686956145a5565b519281519381613e2086935180926020808701910161419e565b820190602082015203602081018452018261417b565b92019080613cf0565b50915080613cf5565b60046040517fbf85e051000000000000000000000000000000000000000000000000000000008152fd5b613e84919250606460043501356149e6565b90386137b9565b506064600435013515156137b4565b503461080f57602060031936011261080f577f47487988ae8c1417174bb9698afe3a54ef641a5255053a53eabc4d188fe6875d6020613ed76141e6565b6001600160a01b0390613eef826001541633146144fe565b168073ffffffffffffffffffffffffffffffffffffffff196017541617601755604051908152a180f35b503461080f57602060031936011261080f5760406020916001600160a01b03613f406141e6565b168152601183522054604051908152f35b503461080f57608060031936011261080f57613f6b6141e6565b50613f746141fc565b5060643567ffffffffffffffff811161080757613f9590369060040161423c565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b823461080f578060031936011261080f576005548282613fe0836140a0565b9283835260209460019186838216918260001461407d575050600114614023575b505061400f9250038361417b565b6106e06040519282849384528301906141c1565b90859250600582527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db05b85831061406557505061400f93508201018580614001565b8054838901850152879450869390920191810161404d565b9150935061400f95925060ff1991501682840152151560051b8201018580614001565b90600182811c921680156140d0575b60208310146140ba57565b634e487b7160e01b600052602260045260246000fd5b91607f16916140af565b6060810190811067ffffffffffffffff8211176124de57604052565b67ffffffffffffffff81116124de57604052565b60c0810190811067ffffffffffffffff8211176124de57604052565b610100810190811067ffffffffffffffff8211176124de57604052565b6040810190811067ffffffffffffffff8211176124de57604052565b60e0810190811067ffffffffffffffff8211176124de57604052565b90601f601f19910116810190811067ffffffffffffffff8211176124de57604052565b60005b8381106141b15750506000910152565b81810151838201526020016141a1565b90601f19601f6020936141df8151809281875287808801910161419e565b0116010190565b600435906001600160a01b03821682036104e757565b602435906001600160a01b03821682036104e757565b604435906001600160a01b03821682036104e757565b35906001600160a01b03821682036104e757565b9181601f840112156104e75782359167ffffffffffffffff83116104e757602083818601950101116104e757565b67ffffffffffffffff81116124de5760051b60200190565b91908260c09103126104e75760405161429a8161410a565b60a0808294803584526142af60208201614228565b60208501526040810135604085015260608101356060850152608081013560808501520135910152565b9190610100838203126104e7576040516142f281614126565b80936142fd81614228565b82526020808201358184015261431560408301614228565b604084015261432660608301614228565b60608401526080820135608084015260a082013560a084015260c091828101358385015260e08101359067ffffffffffffffff82116104e757019084601f830112156104e75781356143778161426a565b95614385604051978861417b565b81875282858189019302850101938185116104e7578301915b8483106143b15750505050505060e00152565b8386916143be8486614282565b81520192019161439e565b610100918281016001600160a01b03918284511681526020908185015182820152604094848682015116868301526060918583830151168382015260809586830151878301528461012060a093848601518582015260e060c096878101518884015201519b60e08201528b51809952019901966000945b878610614454575050505050505050505090565b885180518c528088015184168c890152808b01518c8c0152808201518c830152808301518c8401528401518b8501529984019997860197600190950194614440565b9181601f840112156104e75782359167ffffffffffffffff83116104e7576020808501948460051b0101116104e757565b906003196040818401126104e757600435926024359167ffffffffffffffff83116104e75782610100920301126104e75760040190565b1561450557565b606460405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152fd5b903590601e19813603018212156104e7570180359067ffffffffffffffff82116104e7576020019181360383136104e757565b67ffffffffffffffff81116124de57601f01601f191660200190565b80511561340f5760200190565b805182101561340f5760209160051b010190565b6000467f00000000000000000000000000000000000000000000000000000000000000010361460757507f24fc88ca4cc577be38175f01dd694014086415b93c7410ba2b863edc7b9e876490565b6040516005549190818161461a856140a0565b918282526020958683019560019188838216918260001461471f5750506001146146c5575b505061464d9250038261417b565b51902090604051908101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260408201527fad7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a560608201524660808201523060a082015260a081526146bf8161410a565b51902090565b90879250600582527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db05b85831061470757505061464d9350820101388061463f565b805483880185015286945088939092019181016146ef565b9150935061464d95925060ff199150168652151560051b820101388061463f565b6040519061474d826140da565b600254825260035460208301526004546040830152565b919081101561340f5760051b0190565b6040519061478182614143565b6007546001600160a01b031682526008546020830152565b919082018092116147a657565b634e487b7160e01b600052601160045260246000fd5b6004356001600160a01b03811681036104e75790565b356001600160a01b03811681036104e75790565b6001600160a01b0316156147f657565b60046040517f92863bea000000000000000000000000000000000000000000000000000000008152fd5b6040519061482d82614126565b606060e083600080825280602083015280604083015280848301528060808301528060a083015260c08201520152565b1561486457565b606460405162461bcd60e51b815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61813603018212156104e7570190565b9035907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe61813603018212156104e7570190565b6040519061491b8261410a565b8160a06000918281528260208201528260408201528260608201528260808201520152565b6040519061494d82614143565b600182528160005b602090818110156149775760209161496b61490e565b90828501015201614955565b505050565b919082039182116147a657565b9291926149958261457c565b916149a3604051938461417b565b8294818452818301116104e7578281602093846000960137010152565b9064496cebb80091816000190481118202158302156104e7570290808204910615150190565b9061271091816000190481118202158302156104e7570290808204910615150190565b9160008093602095606494604051946323b872dd60e01b86526001600160a01b03809216600487015216602485015260448401525af13d15601f3d1160016000511416171615614a5557565b606460405162461bcd60e51b815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b9080601f830112156104e757816020614ab493359101614989565b90565b91906040838203126104e75760405190614ad082614143565b8193614adb81614228565b835260208101359167ffffffffffffffff83116104e757602092614aff9201614a99565b910152565b9190916101a09081818503126104e75760405167ffffffffffffffff928101838111828210176124de57604052809482358252602090614b45828501614228565b8284015260408401356040840152614b5f60608501614228565b606084015260808401356080840152614b7a60a08501614228565b60a084015260c084013560c0840152614b9560e08501614228565b60e084015261010080850135908401526101208085013590840152610140808501359084015261016080850135908401526101809384810135908682116104e7570181601f820112156104e7578035614bed8161426a565b96614bfb604051988961417b565b818852848089019260051b840101938085116104e757858401925b858410614c2857505050505050500152565b83358381116104e7578791614c42848480948a0101614ab7565b815201930192614c16565b908160209103126104e7575180151581036104e75790565b903590601e19813603018212156104e7570180359067ffffffffffffffff82116104e757602001918160051b360383136104e757565b601f8260209493601f19938186528686013760008582860101520116010190565b60c0810135908115614cfa575003614cd057565b60046040517fe69ab6be000000000000000000000000000000000000000000000000000000008152fd5b9190610180830191614d0c8385614c65565b8091501580615054575b15614d455760046040517fe69ab6be000000000000000000000000000000000000000000000000000000008152fd5b6001811480615019575b6150125791909493926000908035915b848110614d70575050505050509050565b614d7a8287614c65565b82101561340f5760059082821b810135603e199182813603018212156104e757614da691369101614ab7565b906001600160a01b03808351169160208094015190833b156104e75789958c601e1996604097889283519a7fdbe33d66000000000000000000000000000000000000000000000000000000008c5288614e308d60609081600482015260a46102048201998735606484015285614e1d8b8a01614228565b1660848401528701359101528401614228565b1660c48d0152608082013560e48d015288614e4d60a08401614228565b166101048d01526101248c015287614e6760e08301614228565b166101448c01526101008101356101648c01526101208101356101848c01526101408101356101a48c01526101608101356101c48c01523603018c12156104e7578e8c018035928282019767ffffffffffffffff958686116104e75785901b918236038a136104e757948094939296956101a06101e48f9a949a015252610224808d01968d01019896600091605e1984360301935b868410614f72575050505050505050505091846000818f9b9c9d9e9f81958397614f369260248501526003198483030160448501526141c1565b03925af1908115614f6857509060019291614f59575b5001949392969594614d5f565b614f62906140f6565b38614f4c565b513d6000823e3d90fd5b9091929394959697989a7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffddc8f8203018a528b35868112156104e75782018e85614fbc8a8401614228565b1683528101359084813603018212156104e7570187810135908f018a82116104e75781360381136104e7578f89929183928286615000948660019901520191614c9b565b9d019a01989796959401929190614efc565b5050505050565b506150248486614c65565b1561340f57803590603e19813603018212156104e7576001600160a01b039161504d91016147d2565b1615614d4f565b50811515614d16565b908160209103126104e757517fffffffff00000000000000000000000000000000000000000000000000000000811681036104e75790565b9291909260426150a36145b9565b6040958651917f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201522092813b15156000146151d85791602091615135936001600160a01b0387518096819582947f1626ba7e000000000000000000000000000000000000000000000000000000009a8b855260048501528b60248501526044840191614c9b565b0392165afa9081156151cd57907fffffffff000000000000000000000000000000000000000000000000000000009160009161519f575b5016036151765750565b600490517fd9681a86000000000000000000000000000000000000000000000000000000008152fd5b6151c0915060203d81116151c6575b6151b8818361417b565b81019061505d565b3861516c565b503d6151ae565b83513d6000823e3d90fd5b926151ec6151fb936151f293953691614989565b90615210565b909291926152dc565b6001600160a01b038091169116036151765750565b81519190604183036152415761523a92506020820151906060604084015193015160001a9061524c565b9192909190565b505060009160029190565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084116152d057926020929160ff608095604051948552168484015260408301526060820152600092839182805260015afa156152c45780516001600160a01b038116156152bb57918190565b50809160019190565b604051903d90823e3d90fd5b50505060009160039190565b600481101561539457806152ee575050565b600181036153205760046040517ff645eedf000000000000000000000000000000000000000000000000000000008152fd5b6002810361535957602482604051907ffce698f70000000000000000000000000000000000000000000000000000000082526004820152fd5b6003146153635750565b602490604051907fd78bce0c0000000000000000000000000000000000000000000000000000000082526004820152fd5b634e487b7160e01b600052602160045260246000fd5b9190916101209081818503126104e7576040519182019367ffffffffffffffff94838110868211176124de5760405282948235845260209081840135828601526153f660408501614228565b60408601526060840135606086015260808401359081116104e75783019180601f840112156104e757823561542a8161426a565b93615438604051958661417b565b818552838086019260051b8201019283116104e7578301905b82821061548b5750505050608083015260a081013560a083015260c081013560c083015260e081013560e083015261010080910135910152565b81358152908301908301615451565b81156154a4570490565b634e487b7160e01b600052601260045260246000fd5b818102929181159184041417156147a657565b903590605e19813603018212156104e7570190565b903590601e19813603018212156104e7570180359067ffffffffffffffff82116104e7576020019160c08202360383136104e757565b908160209103126104e757516001600160a01b03811681036104e75790565b939590949194600094600097889461555c60808a0197610e57610e4e8a51809361497c565b428111610f2257506040890199615574838c5161497c565b9a80519861558860a08d019a8b51906154ba565b998d1561565e578d61332360209361331d6155a6945191514261497c565b9301518061564b575b5060608b019182519151908d80600019048411021582021561080f5750908c95949392918682020493615617575b505061560e87610e496155f861561598979695602095614799565b809d6001600160a01b0395869101511696614799565b9316614a09565b565b6020929a50610e496155f88561560e946156386156159b9a99988e9661497c565b905260019d9550959697985050506155dd565b615657919850836149e6565b96386155af565b50919b508b9a50600199508a9750505050505050565b61567d82615d37565b81600052601460205260406000205403610d7457508060c060a06156a693015191015190614799565b42116156ae57565b60046040517f6d4241c0000000000000000000000000000000000000000000000000000000008152fd5b9060a0820135159081156157ce575b506104705760e081013580421161579d57506157076040823592016147d2565b6001600160a01b03811682600082815260106020526040812082825260205260ff604082205416928315615785575b505050615741575050565b6040517f0d7043a00000000000000000000000000000000000000000000000000000000081526001600160a01b039190911660048201526024810191909152604490fd5b60409293508152601160205220541015823880615736565b602490604051907f2f06d7780000000000000000000000000000000000000000000000000000000082526004820152fd5b60e091500151516157e26080830183614c65565b9190501415386156e7565b906157f661490e565b506001600160a01b036040830151169160c060a0820151910151926040519461581e8661410a565b855260208501526040840152606083015242608083015260a082015290565b601554907f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821682036147a657614ab49160011b9061549a565b9060208401511515806158b0575b615890575b50505050565b6158a7936001600160a01b03809151169216614a09565b3880808061588a565b50821515615885565b6024356fffffffffffffffffffffffffffffffff811681036104e75790565b6044356fffffffffffffffffffffffffffffffff811681036104e75790565b815115615a3057600092338452601260205260ff6040852054161592831561596d575b5050506159435760406fffffffffffffffffffffffffffffffff91338152601360205220541690565b60046040517f4eba9946000000000000000000000000000000000000000000000000000000008152fd5b6159cf9293509060209160405193849283926159b87f628b220700000000000000000000000000000000000000000000000000000000988986526060600487015260648601906143c9565b9160248501526003198483030160448501526141c1565b038186335af19081156129b357907fffffffff00000000000000000000000000000000000000000000000000000000918491615a12575b5016141538808061591a565b615a2a915060203d81116151c6576151b8818361417b565b38615a06565b505050600090565b9091615a45368285614989565b5115615a3057600091338352601260205260409360ff858520541615928315615aa9575b505050615a80573381526013602052205460801c90565b600482517f4eba9946000000000000000000000000000000000000000000000000000000008152fd5b615b049293509060209186519384928392615af27f94a2d3d500000000000000000000000000000000000000000000000000000000988986528b600487015260448601906143c9565b91600319858403016024860152614c9b565b038186335af1908115615b6557907fffffffff00000000000000000000000000000000000000000000000000000000918491615b47575b50161415388080615a69565b615b5f915060203d81116151c6576151b8818361417b565b38615b3b565b84513d85823e3d90fd5b6000919060605b6101808201518051851015615c385784615b8f916145a5565b516001600160a01b0381511660208092015182815191012091604090815193818501937f4def3e04bd42194484d5f8a5b268ec0df03b9d9d0402606fe3100023c5d79ac4855283860152606085015260608452608084019380851067ffffffffffffffff8611176124de57600195615c30948685528251902060a091615c1d8151809287868801910161419e565b830191820152039081018452018261417b565b930192615b76565b509192508051916001600160a01b0391826020820151169260408201519080606084015116906080840151908060a0860151169160c08601519160e08701511692610100918288015195610120890151976101606101408b01519a01519a602081519101209b60606040519e8f907f891e530ed2768a9decac48f4b7beec447f755ce23feeeeb952e429145b44ba9160208301526040820152015260808d015260a08c015260c08b015260e08a01528801526101208701526101408601526101608501526101808401526101a08301526101c0908183015281526101e0810181811067ffffffffffffffff8211176124de576040526020815191012090565b90600060605b60e08401518051831015615e095790615e01615d5b846001946145a5565b5160206001600160a01b03818301511660409283810151906060810151608091828101519160a08092015193885195888701977f8ca047c2f10359bf4a27bd2c623674be3801153b6b2646ba08593dc96ad7bb4489528a880152606087015285015283015260c090818301528152615dd28161415f565b5190209382519481615ded879351809286808701910161419e565b82019083820152039081018452018261417b565b910190615d3d565b50919290506001600160a01b038082511692602083015192826040820151169260608201511660808201519060c060a08401519301519360208151910120946040519660208801987f35f73c5cb07b3fa605378d4f576769166fed212ec3813ac1f1d73ef1c537eb0e8a5260408901526060880152608087015260a086015260c085015260e084015261010083015261012090818301528152610140810181811067ffffffffffffffff8211176124de576040525190209056fea2646970667358221220eba2315c8271639a56006b67768be0c0c6e366fb6841900f2b4b05edcbe1d25864736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000097d34635b605c2f1630d6b4c6c5d222b8a2ca47d000000000000000000000000d07a25e6a22e9158162699490aa6a9464e14c50a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000004150ded32a6d3bfecae76e7558af48019034492700000000000000000000000052ac424ef7b283aa5badb8c6254832e3280d7398000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000447e69651d841bd8d104bed4930000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : loanLiquidator (address): 0x97D34635b605C2F1630D6b4c6C5D222B8a2ca47D
Arg [1] : protocolFee (tuple):
Arg [1] : recipient (address): 0xD07a25E6a22e9158162699490Aa6A9464E14c50a
Arg [2] : fraction (uint256): 0
Arg [2] : currencyManager (address): 0x4150deD32A6D3bfecAE76e7558Af480190344927
Arg [3] : collectionManager (address): 0x52Ac424eF7B283aA5bADB8c6254832E3280d7398
Arg [4] : maxSources (uint256): 10
Arg [5] : minLockPeriod (uint256): 500
Arg [6] : delegateRegistry (address): 0x00000000000000447e69651d841bD8D104Bed493
Arg [7] : flashActionContract (address): 0x0000000000000000000000000000000000000000
-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 00000000000000000000000097d34635b605c2f1630d6b4c6c5d222b8a2ca47d
Arg [1] : 000000000000000000000000d07a25e6a22e9158162699490aa6a9464e14c50a
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [3] : 0000000000000000000000004150ded32a6d3bfecae76e7558af480190344927
Arg [4] : 00000000000000000000000052ac424ef7b283aa5badb8c6254832e3280d7398
Arg [5] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [6] : 00000000000000000000000000000000000000000000000000000000000001f4
Arg [7] : 00000000000000000000000000000000000000447e69651d841bd8d104bed493
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode Sourcemap
582:29526:25:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::i;:::-;;10246:21;;;10223:45;582:29526;;10223:45;:::i;:::-;10185:35;582:29526;;10185:35;582:29526;10185:83;10181:207;;582:29526;-1:-1:-1;;;;;10415:26:25;;582:29526;;10415:26;;:::i;:::-;582:29526;10401:10;:40;10397:103;;10534:5;582:29526;;;;10145:26;582:29526;10534:5;:::i;:::-;10597;582:29526;;;;;10597:5;:::i;:::-;10618:28;582:29526;;10618:28;582:29526;10614:109;;10736:23;582:29526;;10736:23;582:29526;10736:27;;10732:97;;582:29526;;;;;;;:::i;:::-;22612:28;;22654:20;;;;582:29526;22654:24;;;;:55;;;582:29526;22650:171;;582:29526;22831:19;;;;;10977:4;22865:13;22860:1096;22865:13;22884:35;;582:29526;;22884:35;582:29526;;;;22884:35;:::i;:::-;22880:46;;;;;;;22966:15;:12;582:29526;22966:12;;;:15;:::i;:::-;;23423;23280:251;582:29526;23021:38;582:29526;23021:35;22884;582:29526;;22884:35;582:29526;;;;23021:35;:::i;:::-;:38;;:::i;:::-;582:29526;;;23315:26;10415;582:29526;;10415:26;23315;:::i;:::-;23359:22;23423:32;-1:-1:-1;;;;;23359:22:25;;;1590:5:24;582:29526:25;23423:15;23441:14;23423:15;;;582:29526;23441:14;;582:29526;23423:32;;:::i;:::-;23280:251;;:::i;:::-;23593:13;;;;;;;582:29526;23636:17;582:29526;29680:10;;;29659:142;;;;22865:13;29642:234;;;;10977:4;23669:37;23720:19;23806:35;23669:37;;23855:30;23669:37;23753:39;23669:37;;:::i;:::-;23720:19;;:::i;:::-;23753:39;;:::i;23806:35::-;23855:30;;:::i;:::-;582:29526;;22865:13;;;;;;;29642:234;582:29526;;;29833:32;;;;29659:142;582:29526;;;29712:30;23608:26;582:29526;;23608:26;582:29526;29712:30;;:::i;:::-;1590:5:24;1744:340:10;;;;;;;;;;;;;;29711:89:25;29659:142;;;;1744:340:10;582:29526:25;1744:340:10;;22880:46:25;;;;;24061:11;22880:46;;-1:-1:-1;;;;;23359:22:25;;;1590:5:24;582:29526:25;24015:26;10415;582:29526;;10415:26;24015;:::i;:::-;24061:11;;:::i;:::-;10185:35;582:29526;;10185:35;582:29526;10997:49;10993:119;;11143:11;582:29526;11125:29;;11121:103;;10977:4;13301:21:24;582:29526:25;;;;13301:21:24;582:29526:25;;-1:-1:-1;;582:29526:25;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;;11369:73;582:29526;;;;;;;;:::i;:::-;11369:73;:::i;:::-;11353:89;;;:::i;:::-;;;;;:::i;:::-;;10977:4;11534:13;;;11553:35;22884;582:29526;;22884:35;582:29526;;;;11553:35;:::i;:::-;11549:46;;;;;;;11616:38;582:29526;11616:35;22884;582:29526;;22884:35;582:29526;;;;11616:35;:::i;:38::-;582:29526;11612:271;;11534:13;10977:4;582:29526;11534:13;;11612:271;22966:12;10977:4;22966:12;;11694:15;22966:12;582:29526;22966:12;;11694;:15;:::i;:::-;;11678:31;;;;:::i;:::-;;;;;;:::i;:::-;;11759:38;582:29526;11759:35;22884;582:29526;;22884:35;582:29526;;;;11759:35;:::i;:38::-;582:29526;;11727:13;;;;:::i;:::-;;:29;582:29526;;11612:271;;;;;11549:46;;12116:80;11549:46;;582:29526;22966:12;;11963:25;12019:12;;;:::i;:::-;582:29526;;;11999:6;22654:20;582:29526;;;;;;;;10145:26;582:29526;;;;;;;;;;;;;;;;;;;;;;10145:26;582:29526;22654:20;582:29526;;;;;;;;23423:15;23359:22;582:29526;;;;23423:15;582:29526;;;;:::i;:::-;;10246:21;582:29526;;;12116:80;;;582:29526;;;;;;;;;22654:20;582:29526;;;;;;;;:::i;:::-;;;;;22654:20;582:29526;;;;;:::i;:::-;;;;;;;;;;;;;11121:103;582:29526;;;;11177:36;;;;582:29526;11177:36;;582:29526;11177:36;22650:171;22744:66;;;;;:::i;:::-;22650:171;;;;;22654:55;;;;;10732:97;582:29526;;;10786:32;;;;10614:109;582:29526;;;10673:39;;;;10397:103;582:29526;;;10464:25;;;;10181:207;582:29526;;;;;10291:86;;;;10185:35;582:29526;;10185:35;582:29526;;10291:86;;582:29526;;;;;10291:86;582:29526;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;-1:-1:-1;;582:29526:25;;:::i;:::-;778:5:7;582:29526:25;;-1:-1:-1;;;;;582:29526:25;756:44:7;582:29526:25;;;764:10:7;:19;756:44;:::i;:::-;582:29526:25;;;;;;778:5:7;582:29526:25;764:10:7;1424:42;;;;582:29526:25;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;8158:30:24;;582:29526:25;;8083:10:24;582:29526:25;;8103:16:24;582:29526:25;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;8083:10:24;;;;8158:30;582:29526:25;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;;;;8158:30:24;;;;582:29526:25;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;:::i;:::-;;;;;;;;-1:-1:-1;;;;;756:44:7;582:29526:25;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;919:9:26;;;:::i;:::-;1590:5:24;;;;;943:11:26;;:::i;:::-;1590:5:24;943:24:26;:53;;;;;582:29526:25;939:110:26;;;582:29526:25;;;;;1058:30:26;582:29526:25;;;;;778:5:7;-1:-1:-1;;582:29526:25;;;;;;1116:14:26;582:29526:25;;;;;;;;:::i;:::-;1590:5:24;582:29526:25;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;1590:5:24;;;582:29526:25;;;;;;;;;;;1590:5:24;;;582:29526:25;;;;;;;;;1164:49:26;582:29526:25;;;1164:49:26;582:29526:25;;939:110:26;582:29526:25;;;1019:19:26;;;;943:53;971:12;;;;;:::i;:::-;1590:5:24;971:25:26;943:53;;;582:29526:25;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;18115:11;582:29526;;;;:::i;:::-;18115:11;:::i;:::-;582:29526;;;;;;18130:6;582:29526;;;;;;18115:30;18111:93;;582:29526;18231:13;582:29526;-1:-1:-1;;;;;18231:13:25;;;;:::i;:::-;582:29526;18217:10;:27;18213:92;;582:29526;;;;;;18314:17;582:29526;;;18371:25;582:29526;18371:25;;;:::i;:::-;582:29526;;;18314:136;;;;;-1:-1:-1;;;18314:136:25;;582:29526;18314:136;;582:29526;18314:136;;582:29526;;;;;;18398:25;582:29526;;;;;;;;;;;;;;;;18314:136;;;;;;;;582:29526;;;18466:37;582:29526;;;;;;;;;;;;;;;;18466:37;582:29526;;18314:136;;;;;;;;;;;;;;;;;:::i;:::-;;;582:29526;;;;18314:136;;;;;;;;;;;582:29526;;1590:5:24;582:29526:25;;1590:5:24;;;;18213:92:25;582:29526;;;18267:27;;;;18111:93;582:29526;;;;18168:25;-1:-1:-1;;;18168:25:25;;582:29526;18168:25;;582:29526;18168:25;582:29526;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;11101:19:24;582:29526:25;-1:-1:-1;;;;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;6671:15:24;582:29526:25;;;;6649:10:24;;:38;6645:121;;582:29526:25;;17590:12;582:29526;;;;:::i;17590:12::-;582:29526;;;17606:6;582:29526;;;;;;17590:31;17586:94;;17695:23;582:29526;;;;;;17695:23;582:29526;;17606:6;582:29526;;;;;;;;;17586:94;582:29526;;;;17644:25;-1:-1:-1;;;17644:25:25;;582:29526;17644:25;;582:29526;17644:25;6645:121:24;582:29526:25;;;;6710:45:24;;;;582:29526:25;6710:45:24;;582:29526:25;6710:45:24;582:29526:25;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;12453:5;;;;:::i;:::-;582:29526;12474:12;;;582:29526;12474:12;;582:29526;12474:23;12470:89;;12608:25;:15;:12;;:15;:::i;:::-;;:25;582:29526;12635:15;30010:89;12635:15;;;582:29526;12653:14;30029:70;30030:31;12653:14;12635:32;12653:14;;;582:29526;;;12635:32;;:::i;:::-;30030:31;:::i;:::-;30072:14;582:29526;30029:70;;:::i;:::-;30010:89;;:::i;:::-;12697:15;12682:30;;12678:111;;12802:12;582:29526;12802:15;-1:-1:-1;;;;;12802:12:25;;:15;:::i;:::-;;:22;1590:5:24;582:29526:25;12828:10;12802:36;12798:99;;582:29526;12906:28;582:29526;13076:51;582:29526;;12906:28;:::i;:::-;582:29526;;;13301:21:24;582:29526:25;;;;13301:21:24;582:29526:25;13015:12;;;:::i;:::-;582:29526;;;12995:6;582:29526;;;;;;;;;;;;;;;;;;;;;;;;;12608:25;582:29526;;;;;12608:25;582:29526;;;;:::i;:::-;;;;;;13076:51;;;582:29526;;;;;;;;;;;;;;;;;;:::i;12678:111::-;582:29526;;;;12735:43;;;;582:29526;12735:43;;582:29526;12735:43;12470:89;582:29526;;;12520:28;;;;582:29526;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;-1:-1:-1;;;;;7948:18:24;582:29526:25;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;21405:51;582:29526;;;:::i;:::-;-1:-1:-1;;;;;582:29526:25;756:44:7;582:29526:25;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;582:29526:25;;-1:-1:-1;;21326:63:25;582:29526;;;21326:63;582:29526;;;;;;21405:51;582:29526;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;10043:10:24;582:29526:25;;10104:29:24;582:29526:25;;;;;;10155:53:24;;;;10151:180;;-1:-1:-1;10043:10:24;582:29526:25;;;10104:29:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;10415:60:24;;582:29526:25;;;;10415:60:24;582:29526:25;10151:180:24;582:29526:25;;10231:89:24;;;10043:10;582:29526:25;10231:89:24;;582:29526:25;;;;;;;;;;;;;;10231:89:24;;;;;582:29526:25;;;;;;-1:-1:-1;;582:29526:25;;;;;;19790:14;582:29526;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;-1:-1:-1;;;;;582:29526:25;;:::i;:::-;;;;1751:30:26;582:29526:25;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;19455:11;582:29526;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;1422:6:24;582:29526:25;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;756:44:7;-1:-1:-1;;;;;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;7591:38:24;582:29526:25;;;;7553:22:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;7591:38:24;582:29526:25;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;756:44:7;-1:-1:-1;;;;;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;1422:6:24;12676:35;;12672:95;;582:29526:25;;12834:47:24;582:29526:25;;12776:42:24;582:29526:25;;;12776:42:24;582:29526:25;;;;;;12834:47:24;582:29526:25;;12672:95:24;582:29526:25;;;12734:22:24;;;;582:29526:25;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;1309:4:24;582:29526:25;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;20349:12;582:29526;;;;:::i;20349:12::-;582:29526;;;20365:6;582:29526;;;;;;20349:31;20345:94;;20466:14;582:29526;-1:-1:-1;;;;;20466:14:25;;;;:::i;:::-;582:29526;20452:10;:28;20448:93;;582:29526;20558:26;;;;;;;:::i;:::-;582:29526;;;;20635:20;582:29526;;20658:26;;582:29526;20551:143;;;;;;582:29526;;-1:-1:-1;;;20551:143:25;;20620:4;582:29526;20551:143;;582:29526;-1:-1:-1;;;;;582:29526:25;;;;1590:5:24;;;582:29526:25;1590:5:24;;;582:29526:25;;;;;;;1590:5:24;;582:29526:25;;;;20551:143;;;;;;;;582:29526;;;;20635:20;582:29526;;20733:26;;;;:::i;:::-;20704:100;;;;;582:29526;;;;;;;;;;;20704:100;;;;;;;1590:5:24;20704:100:25;;582:29526;;20704:100;;582:29526;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;20704:100;;;;;;;;;;;;;582:29526;20826:26;;;582:29526;20826:26;;:::i;:::-;582:29526;;;;;20819:70;;;;1590:5:24;20819:70:25;;582:29526;20819:70;;582:29526;20819:70;;;;;;;;;;;582:29526;20620:4;;582:29526;;20819:87;20815:146;;20976:44;582:29526;;;;;;;;;;;;;;;;;;;;;;;;:::i;20815:146::-;582:29526;;;20929:21;;;;20819:70;;;;;582:29526;20819:70;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;;582:29526;;1590:5:24;582:29526:25;;1590:5:24;;;;20704:100:25;;;;;;:::i;:::-;582:29526;;20704:100;;;;;582:29526;;;;20704:100;582:29526;;;1590:5:24;;;;;;;;20551:143:25;;;;:::i;:::-;582:29526;;20551:143;;;;;582:29526;;1590:5:24;582:29526:25;;1590:5:24;;;;20345:94:25;582:29526;;;;20403:25;-1:-1:-1;;;20403:25:25;;582:29526;20403:25;;582:29526;20403:25;582:29526;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;-1:-1:-1;;;;;582:29526:25;;:::i;:::-;;;;10800:5:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;-1:-1:-1;;582:29526:25;;;:::i;:::-;;;;;;;;;424:13:23;;;439:15;;;;;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;424:13:23;582:29526:25;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;564:35:23;572:4;;564:35;;582:29526:25;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;540:59:23;;;;:::i;:::-;;617:8;613:51;;582:29526:25;;424:13:23;;;;;613:51;653:10;;;;:::i;:::-;;582:29526:25;;;;634:30:23;;;;;;582:29526:25;634:30:23;;582:29526:25;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;:::i;:::-;9586:13:24;9601:9;;;;;;582:29526:25;;;9586:13:24;9653:20;;;;;:::i;:::-;582:29526:25;9511:10:24;;582:29526:25;;9687:29:24;582:29526:25;;;;;;;;;;9768:52:24;;582:29526:25;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;9511:10:24;;;;9768:52;582:29526:25;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;;;;9768:52:24;;;;582:29526:25;9586:13:24;;582:29526:25;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;20156:6;582:29526;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;-1:-1:-1;;;;;19055:17:25;582:29526;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;9298:53:24;;582:29526:25;;9200:10:24;582:29526:25;;9220:29:24;582:29526:25;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;9200:10:24;;;;9298:53;582:29526:25;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;1363:7:24;582:29526:25;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;-1:-1:-1;;;;;582:29526:25;;:::i;:::-;;;;2977:81:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;-1:-1:-1;;;;;690:20:7;582:29526:25;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;2467:43:24;582:29526:25;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;504:34:11;522:1;582:29526:25;;512:11:11;504:34;:::i;:::-;558:1;582:29526:25;;4522:21;;;;;:::i;:::-;4572:23;;;;;:::i;:::-;582:29526;4632:34;:28;582:29526;;;;4632:28;:::i;:::-;:34;;:::i;:::-;4694:12;-1:-1:-1;;;;;4694:12:25;582:29526;4694:12;;;:::i;:::-;582:29526;4694:46;;;;4786:28;582:29526;;;;4786:28;:::i;:::-;4891:35;;;;;582:29526;;;4891:35;:::i;:::-;4940:37;;;;;;582:29526;;;4940:37;:::i;:::-;14152:27:24;582:29526:25;14152:20:24;;;;:::i;:::-;:27;;:::i;:::-;14208:20;:29;4891:35:25;14208:20:24;;;;:::i;:29::-;14274:20;;;;;:::i;:::-;582:29526:25;;;-1:-1:-1;;;;;582:29526:25;;14350:10:24;:21;14346:112;;4694:46:25;582:29526;;;-1:-1:-1;;;;;582:29526:25;;14350:10:24;14471:23;14467:128;;4694:46:25;14627:20:24;;;;;582:29526:25;14609:15:24;;:38;14605:115;;14751:29;4891:35:25;14751:29:24;;582:29526:25;14609:15:24;;14733:47;14729:133;;582:29526:25;-1:-1:-1;;;;;582:29526:25;;;;14876:16:24;582:29526:25;;;;;;;;;;;;;;;;;14876:72:24;;;;4694:46:25;14872:158:24;;15068:21;;;582:29526:25;15044:21:24;582:29526:25;15044:21:24;;582:29526:25;15044:45:24;15040:147;;15233:22;;582:29526:25;15233:22:24;;;:::i;:::-;-1:-1:-1;;;;;582:29526:25;;15202:54:24;1590:5;15202:54;;582:29526:25;;15202:54:24;;582:29526:25;;15202:16:24;4522:21:25;15202:16:24;-1:-1:-1;;;;;15202:16:24;582:29526:25;15202:54:24;;;;;;;;;;;4694:46:25;15201:55:24;;15197:122;;15366:26;582:29526:25;15366:26:24;;;:::i;:::-;-1:-1:-1;;;;;582:29526:25;;15333:60:24;1590:5;15333:60;;582:29526:25;;15333:60:24;;582:29526:25;;15333:18:24;4522:21:25;15333:18:24;-1:-1:-1;;;;;15333:18:24;582:29526:25;15333:60:24;;;;;;;;;;;4694:46:25;15332:61:24;;15328:130;;-1:-1:-1;;;;;582:29526:25;15472:20:24;;;:43;;;;4694:46:25;15468:101:24;;;-1:-1:-1;;;;;582:29526:25;15582:22:24;;;:49;;;;4694:46:25;15578:109:24;;;15700:14;;;582:29526:25;15700:19:24;15696:76;;15785:12;;;582:29526:25;15785:17:24;15781:74;;4940:37:25;15869:14:24;;582:29526:25;15869:18:24;;;15868:97;;;;4694:46:25;15864:161:24;;;582:29526:25;16059:22:24;;;;;582:29526:25;16059:22:24;;:::i;:::-;522:1:11;13301:21:24;582:29526:25;;;;13301:21:24;582:29526:25;;5063:28;582:29526;;;;;;5063:28;:::i;:::-;:35;582:29526;5134:15;;;:::i;:::-;582:29526;;;;;:::i;:::-;;;;-1:-1:-1;;;;;582:29526:25;;5171:64;;;582:29526;5171:64;582:29526;5171:64;;582:29526;5171:64;4891:35;5171:64;;582:29526;14609:15:24;4940:37:25;5171:64;;582:29526;15785:12:24;5222::25;;582:29526;;5171:64;;582:29526;5159:76;;;:::i;:::-;;;;;:::i;:::-;-1:-1:-1;;;;;;582:29526:25;5304:28;582:29526;;;;5304:28;:::i;:::-;:36;582:29526;5354:26;;5394:22;582:29526;5354:26;582:29526;5354:26;;;:::i;:::-;5394:22;;;:::i;:::-;582:29526;;;;;;;;:::i;:::-;;;;;;5264:259;582:29526;;;5264:259;;582:29526;;4891:35;5264:259;;582:29526;5264:259;4940:37;5264:259;;582:29526;14609:15:24;582:29526:25;5264:259;;582:29526;15700:14:24;5479::25;;582:29526;;5264:259;;582:29526;;5264:259;;582:29526;5551:11;;;:::i;:::-;582:29526;;;5534:6;582:29526;;;;;;15068:21:24;5613::25;;582:29526;2274:488:10;-1:-1:-1;;2274:488:10;582:29526:25;5586:9;;582:29526;2274:488:10;;;;;;;;;5586:9:25;;;582:29526;5586:9;;582:29526;2274:488:10;;;;;;;;;;582:29526:25;;:::i;:::-;5738:22;582:29526;5394:22;;5738;:::i;:::-;5783:20;582:29526;5783:20;;582:29526;;;;;5770:46;;;;:::i;:::-;5818:11;;;;:::i;:::-;5856:22;582:29526;5394:22;;5856;:::i;:::-;-1:-1:-1;;;;;582:29526:25;5915:12;;;;;;:::i;:::-;;;;;:::i;:::-;6134:28;582:29526;;;;6134:28;:::i;:::-;4940:37;6134:41;;;;;:::i;:::-;582:29526;1590:5:24;;;;:::i;:::-;6090:91:25;;;;;:::i;:::-;1590:5:24;;6195:7:25;6191:415;;4694:46;5354:26;;;6689:28;-1:-1:-1;;;;;6623:26:25;582:29526;;5354:26;;6623;:::i;:::-;582:29526;;;;;6689:28;:::i;:::-;:36;582:29526;6616:110;;;;;582:29526;;-1:-1:-1;;;6616:110:25;;-1:-1:-1;;;;;582:29526:25;;;6616:110;;582:29526;6682:4;1590:5:24;;;582:29526:25;1590:5:24;;;582:29526:25;;;;;;;;;1590:5:24;;582:29526:25;;;;6616:110;;;;;;;;4694:46;582:29526;;6742:69;582:29526;;4940:37;582:29526;-1:-1:-1;;;;;522:1:11;582:29526:25;;;;;;1590:5:24;582:29526:25;;;;;;;1590:5:24;582:29526:25;1590:5:24;;582:29526:25;;;1590:5:24;;;582:29526:25;1590:5:24;;;;:::i;:::-;582:29526:25;;4891:35;1590:5:24;;582:29526:25;;1590:5:24;;;582:29526:25;;5586:9;;582:29526;;1590:5:24;;582:29526:25;6742:69;;;6826:14;582:29526;6826:18;4940:37;;-1:-1:-1;;;;;582:29526:25;;;6860:5;582:29526;;;;;;;;;;6860:39;582:29526;;;;;;6860:39;:::i;:::-;582:29526;;6822:166;582:29526;;;;;;;;;;;;;;;;;;;:::i;6822:166::-;-1:-1:-1;;;;;582:29526:25;;;;;;14876:16:24;582:29526:25;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;6822:166;;6616:110;;;;;:::i;:::-;582:29526;;6616:110;;;;582:29526;;;6191:415;6299:50;6236:32;6429:16;6236:32;;;:::i;:::-;582:29526;;6299:50;;:::i;:::-;5394:22;;;6429:16;5394:22;-1:-1:-1;;;;;6370:22:25;582:29526;5394:22;;6370;:::i;:::-;582:29526;6429:16;;:::i;:::-;;;:::i;:::-;6464:10;;6460:136;6191:415;6460:136;-1:-1:-1;;;;;5394:22:25;6500;582:29526;6574:6;5394:22;;6500;:::i;:::-;582:29526;1590:5:24;;582:29526:25;6574:6;;:::i;:::-;6460:136;;;;6191:415;;15864:161:24;582:29526:25;;;15988:26:24;;;;15868:97;582:29526:25;;-1:-1:-1;;;;;582:29526:25;;;;15893:5:24;582:29526:25;;;;;;;;;;;15893:54:24;582:29526:25;15044:21:24;;582:29526:25;;;;;15893:54:24;:::i;:::-;:71;15868:97;;;15781:74;582:29526:25;;;15825:19:24;;;;15696:76;582:29526:25;;;15742:19:24;;;;15578:109;582:29526:25;;;15654:22:24;;;;15582:49;582:29526:25;;-1:-1:-1;;;;;582:29526:25;;15609:21:24;;15582:49;;;15468:101;582:29526:25;;;15538:20:24;;;;15472:43;582:29526:25;;-1:-1:-1;;;;;582:29526:25;;15497:17:24;;15472:43;;;15328:130;582:29526:25;;;15416:31:24;;;;15333:60;;;;582:29526:25;15333:60:24;582:29526:25;15333:60:24;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;582:29526:25;;1590:5:24;582:29526:25;;1590:5:24;;;;15197:122;582:29526:25;;;15279:29:24;;;;15202:54;;;;582:29526:25;15202:54:24;582:29526:25;15202:54:24;;;;;;;:::i;:::-;;;;15040:147;582:29526:25;4572:23;582:29526;;;;15112:64:24;;;;15044:21;582:29526:25;;15112:64:24;;582:29526:25;4522:21;582:29526;;;15112:64:24;14872:158;582:29526:25;;14971:48:24;;;-1:-1:-1;;;;;582:29526:25;;;14971:48:24;;582:29526:25;;;;;;;;;10231:89:24;14876:72;582:29526:25;-1:-1:-1;;;;;582:29526:25;;;;14927:10:24;582:29526:25;;;;;;;;14916:31:24;;14876:72;;14729:133;4522:21:25;582:29526;;;14803:48:24;;;;582:29526:25;14803:48:24;;582:29526:25;14803:48:24;14467:128;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;4891:35;582:29526;;;4891:35;582:29526;;;;4940:37;582:29526;;;;;;;14560:23:24;582:29526:25;;;;4940:37;582:29526;;;;;;:::i;:::-;4940:37;582:29526;;;;;4291:26:27;4296:20;;4291:26;:::i;:::-;582:29526:25;;;;;;4891:35;582:29526;;;;;;;4471:27:27;;582:29526:25;;;;;4461:38:27;582:29526:25;;;4225:288:27;582:29526:25;4225:288:27;;582:29526:25;1387:66:27;582:29526:25;;;1387:66:27;;582:29526:25;4891:35;1387:66:27;;582:29526:25;4940:37;1387:66:27;;582:29526:25;;1387:66:27;;582:29526:25;1387:66:27;;;582:29526:25;1387:66:27;4225:288;;;;;:::i;:::-;582:29526:25;4202:321:27;;14560:23:24;;:::i;:::-;14467:128;;;;582:29526:25;;;;;-1:-1:-1;;;582:29526:25;;;;;4522:21;582:29526;;;;;;14346:112:24;14411:12;582:29526:25;14425:21:24;582:29526:25;;;;:::i;:::-;14411:12:24;:::i;:::-;14425:21;;:::i;:::-;14346:112;;;;;4694:46:25;;;;;582:29526;;;;;;-1:-1:-1;;582:29526:25;;;;;;-1:-1:-1;;;;;12228:15:24;582:29526:25;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;8764:10:24;582:29526:25;;8812:10:24;582:29526:25;;;;;;8842:32:24;;;;8838:123;;-1:-1:-1;8764:10:24;582:29526:25;;;8812:10:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;9016:37:24;;582:29526:25;;;;9016:37:24;582:29526:25;8838:123:24;582:29526:25;;8897:53:24;;;8764:10;582:29526:25;8897:53:24;;582:29526:25;;;;;;;;;;;;;;10231:89:24;582:29526:25;;;;;;-1:-1:-1;;582:29526:25;;;;;;;13013:27:24;582:29526:25;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;-1:-1:-1;;;;;756:44:7;582:29526:25;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;11398:26:24;582:29526:25;1363:7:24;582:29526:25;;;;;;;11380:15:24;:64;11376:143;;582:29526:25;;11578:39:24;582:29526:25;11543:19:24;582:29526:25;;;-1:-1:-1;;11528:34:24;582:29526:25;;;11528:34:24;582:29526:25;;;;;;;;;;;;;;;11578:39:24;582:29526:25;;11376:143:24;582:29526:25;;;;11467:41:24;;;;582:29526:25;11467:41:24;;582:29526:25;11467:41:24;582:29526:25;;;-1:-1:-1;;;582:29526:25;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;19982:37;582:29526;;;756:44:7;-1:-1:-1;;;;;582:29526:25;;;764:10:7;:19;756:44;:::i;:::-;582:29526:25;19934:32;582:29526;;;;;;19982:37;582:29526;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;;12486:51:24;582:29526:25;756:44:7;582:29526:25;;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;12404:23:24;;;:::i;:::-;582:29526:25;;12438:32:24;582:29526:25;;;;;;;;12438:32:24;582:29526:25;;;;;;12486:51:24;582:29526:25;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;1210:49:24;582:29526:25;;;;;;;;;;;:::i;:::-;;504:34:11;522:1;582:29526:25;;512:11:11;504:34;:::i;:::-;558:1;582:29526:25;;16197:12;582:29526;;;;:::i;16197:12::-;582:29526;;;;;;16213:6;582:29526;;;;;;16197:31;16193:94;;16321:32;16339:14;;;582:29526;16321:15;;;582:29526;16321:32;:::i;:::-;582:29526;-1:-1:-1;;;;;16391:26:25;;;582:29526;16391:26;;;:::i;:::-;582:29526;16514:15;;16497:32;;16493:101;;16603:24;582:29526;16641:12;582:29526;16641:12;;;522:1:11;16641:12:25;;;;:::i;:::-;:24;;;16637:767;522:1:11;;;16730:12:25;;;;;:::i;:::-;582:29526;;;16730:22;;;;;:::i;:::-;16681:100;;;;;;582:29526;;-1:-1:-1;;;16681:100:25;;16723:4;582:29526;16681:100;;582:29526;-1:-1:-1;;;;;582:29526:25;;;;1590:5:24;;;582:29526:25;16754:26;;582:29526;1590:5:24;;;582:29526:25;;;;;1590:5:24;;582:29526:25;;;;16681:100;;;;;;;;16637:767;582:29526;;522:1:11;582:29526:25;16801:23;582:29526;;;;;;16801:23;582:29526;;16213:6;582:29526;;;;;;;16637:767;582:29526;;;;;;;;;;;;;;:::i;16681:100::-;;;;;:::i;:::-;582:29526;;16681:100;;;;582:29526;;1590:5:24;582:29526:25;;1590:5:24;;;;582:29526:25;;;-1:-1:-1;;;582:29526:25;;;;;;16637:767;582:29526;;;;16985:15;582:29526;17003:26;;;;582:29526;16928:102;;;;;;582:29526;;-1:-1:-1;;;16928:102:25;;16970:4;582:29526;16928:102;;582:29526;;;;;;;;-1:-1:-1;;;;;582:29526:25;1590:5:24;;;582:29526:25;1590:5:24;;;582:29526:25;;;;;1590:5:24;582:29526:25;;16928:102;;;;;;;;;;;;;;;;16637:767;582:29526;;;;17209:22;582:29526;;16985:15;582:29526;17209:22;;;:::i;:::-;582:29526;;;;17058:260;;;;;1590:5:24;17058:260:25;;;582:29526;17058:260;;582:29526;;;;;;;;;;;;;;;;;;;;;17294:10;582:29526;;;;;;;17058:260;;;;;;;;;;;16637:767;17044:274;582:29526;522:1:11;17044:274:25;;17338:55;17044:274;582:29526;16985:15;582:29526;;;;;;;;;;;;;17338:55;16637:767;;17058:260;;;;;;;;;;;;:::i;:::-;;;582:29526;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;522:1:11;582:29526:25;;;;17338:55;582:29526;;;;;:::i;:::-;17058:260;;;;;;;16928:102;;;;;;;;;:::i;:::-;582:29526;;16928:102;;;;;;;;16493:101;582:29526;;;;16552:31;;;;582:29526;16552:31;;582:29526;16552:31;582:29526;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;-1:-1:-1;;;;;582:29526:25;;:::i;:::-;;;;2690:68:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;-1:-1:-1;;;;;7778:16:24;582:29526:25;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;-1:-1:-1;;;;;21161:20:25;582:29526;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;:::i;:::-;;;:::i;:::-;;;;-1:-1:-1;;;;;582:29526:25;;;;;;18658:37;1590:5:24;18658:37:25;;;582:29526;18658:37;;582:29526;;18658:37;;;582:29526;18658:37;;;;;;;;;;;;;582:29526;-1:-1:-1;582:29526:25;;18707:4;18658:54;18654:112;;582:29526;;;;18776:17;582:29526;;;;;;18776:77;;;;;-1:-1:-1;;;18776:77:25;;582:29526;;;18776:77;;582:29526;;;;;;;;;;;;;;;;;;;;18776:77;;;;;;;;582:29526;-1:-1:-1;;582:29526:25;;;-1:-1:-1;;;;;582:29526:25;;;;;;;;;1590:5:24;;;582:29526:25;1590:5:24;;582:29526:25;;;;18869:48;;582:29526;1590:5:24;;;18869:48:25;1590:5:24;18776:77:25;;;;;;;;;;;;:::i;:::-;;;582:29526;;;;18776:77;;;;;;;;;18654:112;582:29526;;;18735:20;;;;18658:37;;;;;;;;;;;;;;:::i;:::-;;;;582:29526;;;;;;-1:-1:-1;;582:29526:25;;;;;-1:-1:-1;;;;;756:44:7;582:29526:25;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;11758:24:24;582:29526:25;11758:43:24;1309:4;11758:43;;11754:130;;12058:45;11914:25;582:29526:25;11914:25:24;;;;:::i;:::-;;:::i;:::-;582:29526:25;;:::i;:::-;;;;-1:-1:-1;;11951:37:24;582:29526:25;;;11951:37:24;582:29526:25;;;;12027:15:24;11998:44;582:29526:25;;;;;;11758:24:24;582:29526:25;;;12058:45:24;582:29526:25;;11754:130:24;11758:24;582:29526:25;;;11824:49:24;;;;582:29526:25;11824:49:24;;582:29526:25;11824:49:24;582:29526:25;;;;;;-1:-1:-1;;582:29526:25;;;;;1530:45:26;582:29526:25;;;:::i;:::-;-1:-1:-1;;;;;582:29526:25;756:44:7;582:29526:25;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;582:29526:25;;;;1423:30:26;582:29526:25;;;;;-1:-1:-1;;582:29526:25;;;;;1489:14:26;582:29526:25;;;;;;;;;;;;1530:45:26;582:29526:25;;;;;;;;-1:-1:-1;;582:29526:25;;;;;19632:31;582:29526;;;756:44:7;-1:-1:-1;;;;;582:29526:25;;;764:10:7;:19;756:44;:::i;:::-;582:29526:25;19590:26;582:29526;;;;;;19632:31;582:29526;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;:::i;:::-;8392:13:24;8407:9;;;;;;582:29526:25;;;8392:13:24;8451:12;;;;;:::i;:::-;582:29526:25;8325:10:24;;582:29526:25;;8477:16:24;582:29526:25;;;;;;;;;;8535:29:24;;582:29526:25;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;8325:10:24;;;;8535:29;582:29526:25;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;;;;8535:29:24;;;;582:29526:25;8392:13:24;;582:29526:25;;;;;;-1:-1:-1;;582:29526:25;;;;;;11252:26:24;582:29526:25;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;17900:38;582:29526;;17900:38;:::i;:::-;582:29526;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;:::i;:::-;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;504:34:11;522:1;582:29526:25;;512:11:11;504:34;:::i;:::-;558:1;582:29526:25;;13333:19;582:29526;;;;;13333:19;:::i;:::-;582:29526;;;13390:19;582:29526;;13390:19;582:29526;;;;;;;;-1:-1:-1;;;;;13569:13:25;582:29526;;;;;;13569:13;:::i;:::-;582:29526;13555:10;:27;13551:150;;582:29526;13711:29;582:29526;;;;;;;;;:::i;:::-;13711:29;;:::i;:::-;582:29526;13884:19;582:29526;;;;;13884:19;:::i;:::-;:34;582:29526;;;;;;;;13880:215;;582:29526;-1:-1:-1;;;;;14112:25:25;;582:29526;;;;14112:25;;:::i;:::-;582:29526;14167:13;582:29526;;;;;;14167:13;:::i;:::-;14105:103;;;;;582:29526;;-1:-1:-1;;;14105:103:25;;14160:4;582:29526;14105:103;;;582:29526;;;;-1:-1:-1;;;;;582:29526:25;;;;13390:19;1590:5:24;;;582:29526:25;;;;;;;;14182:25;;;582:29526;1590:5:24;;;582:29526:25;;;;;;1590:5:24;;582:29526:25;;;;14105:103;;;;;;;;582:29526;-1:-1:-1;14367:71:25;14405:32;:19;582:29526;;;;;14405:19;:::i;:::-;582:29526;14405:32;;;;:::i;:::-;582:29526;;;;;;;;;;:::i;:::-;14367:71;:::i;:::-;582:29526;;;:::i;:::-;14560:20;582:29526;14560:20;;582:29526;14560:24;;14594:28;;582:29526;-1:-1:-1;;;;;14653:21:25;;582:29526;;;;14653:21;;:::i;:::-;582:29526;14685:26;14726:13;;14721:996;14726:13;14745:11;582:29526;;;;;14745:11;;;;582:29526;14745:11;:::i;:::-;14741:22;;;;;;;14803:11;582:29526;;;;;14745:11;;;;582:29526;14803:11;:::i;:::-;582:29526;;;;;;;;;;;;;;:::i;:::-;14854:22;;1423:69:28;582:29526:25;14854:22;;582:29526;1440:19:28;14889:13:25;;;582:29526;14904:34;14922:16;;;582:29526;14904:15;:34;:::i;:::-;1440:19:28;;:::i;:::-;1423:69;;:::i;:::-;1590:5:24;14967:51:25;1590:5:24;;;582:29526:25;14854:22;;582:29526;14967:51;:::i;:::-;15033:27;;15074:22;;15110:269;;;14726:13;14854:22;;;15429:85;15619:27;14854:22;;15429:79;15594:9;14854:22;15429:61;:98;14854:22;15429:47;582:29526;522:1:11;14854:22:25;;582:29526;;15454:22;;582:29526;15429:47;;:::i;:::-;:61;:::i;:98::-;582:29526;;-1:-1:-1;;;;;582:29526:25;15564:13;582:29526;;;;;;15564:13;:::i;:::-;15579;;1590:5:24;582:29526:25;15594:9;;;:::i;15619:27::-;582:29526;;14726:13;;;15110:269;522:1:11;14560:20:25;;;15594:9;14560:20;;;15429:85;15619:27;14560:20;;15429:79;14560:20;15429:61;:98;14560:20;;15252:46;15167:54;582:29526;15316:48;14560:20;;582:29526;15167:54;;;:::i;:::-;15252:46;;;:::i;:::-;15336:28;;;;;:::i;15316:48::-;15110:269;;;;;;;;;;;;;;;;;582:29526;-1:-1:-1;;;582:29526:25;;;;;13390:19;582:29526;;14741:22;;;;;;;15732:52;582:29526;14741:22;;582:29526;;;;;;;;;;;;;;;15732:52;15795:124;;14721:996;582:29526;;;;15968:6;582:29526;;;;;;;522:1:11;582:29526:25;;;;15795:124;-1:-1:-1;;;;;15853:13:25;582:29526;15891:16;582:29526;;;;;15853:13;:::i;:::-;1590:5:24;;582:29526:25;15891:16;;:::i;:::-;15795:124;;;;;;14105:103;;;;:::i;:::-;;;;13880:215;-1:-1:-1;;;;;13934:17:25;582:29526;;;-1:-1:-1;;;;;582:29526:25;13984:13;582:29526;;;;;;13984:13;:::i;:::-;582:29526;13999:25;;582:29526;;;;13999:25;;:::i;:::-;582:29526;;;;13934:150;;;;;-1:-1:-1;;;13934:150:25;;582:29526;;13934:150;;582:29526;;13390:19;582:29526;;;13390:19;582:29526;;;;14026:25;582:29526;13999:25;582:29526;;;;;;;;522:1:11;582:29526:25;;;;13934:150;;;;;;;;13880:215;;;;13934:150;582:29526;13934:150;;;;;;;;;;;;:::i;:::-;;;582:29526;;;;13934:150;;;;;;;;13551;13614:13;582:29526;;;;;;13614:13;:::i;:::-;13629:19;582:29526;;;;;13629:19;:::i;:::-;582:29526;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4798:38:27;582:29526:25;;4684:213:27;1623:66;582:29526:25;4684:213:27;;582:29526:25;1623:66:27;;;582:29526:25;;1623:66:27;;582:29526:25;;;;;1623:66:27;;;582:29526:25;1623:66:27;4684:213;;582:29526:25;1623:66:27;582:29526:25;;;;1623:66:27;582:29526:25;;;;;;1623:66:27;582:29526:25;;;;;;;4684:213:27;;;4661:246;13657:32:25;;;582:29526;;;13657:32;;;;582:29526;13657:32;:::i;:::-;;;;;:::i;:::-;13551:150;;582:29526;13390:19;582:29526;-1:-1:-1;;;582:29526:25;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;-1:-1:-1;;;;;582:29526:25;;:::i;:::-;;;;2839:45:24;582:29526:25;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;7375:27;7437:5;;;;582:29526;;;7302:26;582:29526;7437:5;:::i;:::-;7500;;;582:29526;;;;7500:5;:::i;:::-;-1:-1:-1;;;;;7556:26:25;582:29526;;;7556:26;;:::i;:::-;582:29526;7355:10;7542:40;;;7712:65;;;22401:18;22421:28;;;22476:27;582:29526;;;:::i;:::-;22556:45;;22612:28;22654:20;;;;;582:29526;22654:24;;;:55;;;582:29526;22650:171;;582:29526;22831:19;22849:1;22831:19;;;22865:13;;;;;22884:35;;582:29526;;22884:35;582:29526;;;;22884:35;:::i;:::-;22880:46;;;;;;;;23423:15;22966;;;582:29526;22966:12;;;;:15;:::i;:::-;;582:29526;23280:251;23021:38;:35;22884;582:29526;;22884:35;582:29526;;;;23021:35;:::i;:::-;:38;;;;:::i;:::-;582:29526;23315:26;582:29526;;;7556:26;23315;:::i;:::-;23359:22;-1:-1:-1;;;;;582:29526:25;23359:22;;;1590:5:24;582:29526:25;23423:15;;;;;;582:29526;23280:251;23423:32;23280:251;;23441:14;;;;;582:29526;23423:32;;:::i;23280:251::-;23593:13;;;;;;;;;582:29526;23636:17;582:29526;29659:31;;;;;;;22865:13;29659:142;;;22865:13;29642:234;;;;23720:19;23806:35;23669:37;;23855:30;23669:37;23753:39;23669:37;;:::i;:::-;23720:19;;:::i;:::-;23753:39;;;;:::i;:::-;;;23806:35;:::i;23855:30::-;23928:3;;;22849:1;582:29526;23928:3;;22865:13;;;23928:3;22865:13;;29659:142;582:29526;;;29712:30;23608:26;582:29526;;23608:26;582:29526;29712:30;;:::i;:::-;1590:5:24;1744:340:10;;;;;;;;;;;;;;29711:89:25;29659:142;;;;1744:340:10;;;;29659:31:25;29680:10;;;;-1:-1:-1;29659:31:25;;22880:46;;;;;24061:11;22880:46;;-1:-1:-1;;;;;582:29526:25;23359:22;;;1590:5:24;582:29526:25;24061:11;;;582:29526;24015:26;582:29526;;;7556:26;24015;:::i;24061:11::-;22849:1;7593:184;;7791:19;;7787:77;;7945:693;;;;;582:29526;8020:35;582:29526;;8020:35;582:29526;8132:15;8101:46;8132:15;8101:28;582:29526;;8101:28;582:29526;8101:46;:::i;:::-;8259:43;8165:32;23441:14;;;;582:29526;23423:15;;;;582:29526;8165:32;;:::i;:::-;8281:21;;;;;582:29526;8259:43;;:::i;:::-;582:29526;;:::i;:::-;19027:44:24;;;;;:::i;:::-;:48;;19026:329;;;;7945:693:25;19008:380:24;;;;;;;;7945:693:25;19008:414:24;;;;7945:693:25;18991:500:24;;;;7945:693:25;8671:61;8709:23;582:29526;;8709:23;582:29526;8671:35;582:29526;;8671:35;582:29526;8671:61;:::i;:::-;8743:119;;;7945:693;8876:25;;;;;;23359:22;9025:25;23359:22;-1:-1:-1;;;;;582:29526:25;23359:22;;;1590:5:24;582:29526:25;8981:14;9025:25;-1:-1:-1;;;;;8981:14:25;;1590:5:24;582:29526:25;;8997:26;582:29526;;;7556:26;8997;:::i;:::-;9025:25;;:::i;:::-;22849:1;13301:21:24;582:29526:25;;;;13301:21:24;582:29526:25;9416:73;9374:15;;;:::i;:::-;582:29526;;;;;;;;:::i;9416:73::-;9400:89;;;:::i;:::-;;;;;:::i;:::-;;582:29526;22966:12;;;9499:25;9551:66;9552:33;23423:15;;;;582:29526;9552:15;:33;:::i;:::-;9589:28;582:29526;;9589:28;582:29526;9551:66;;:::i;:::-;23441:14;;;;582:29526;8671:35;582:29526;;8671:35;582:29526;9627:21;;;;582:29526;9717:12;;;;:::i;:::-;582:29526;;;9697:6;22654:20;582:29526;;;;;;;;7302:26;582:29526;;;;;;;9776:102;582:29526;;;;;;;;;;;;7302:26;582:29526;22654:20;582:29526;;;;;;;;23423:15;582:29526;;;;;;23423:15;582:29526;;;;;:::i;:::-;8709:23;582:29526;;8709:23;582:29526;9627:21;582:29526;;;9776:102;;;582:29526;;;;;;22654:20;582:29526;;;;;;;;;;;:::i;8872:412::-;9085:25;;;9081:203;;8872:412;;;;;9081:203;9234:25;23359:22;-1:-1:-1;;;;;582:29526:25;23359:22;;;1590:5:24;582:29526:25;;9234:25;9190:26;582:29526;;;7556:26;9190;:::i;:::-;9218:14;-1:-1:-1;;;;;9218:14:25;;1590:5:24;582:29526:25;9234:25;;:::i;:::-;9081:203;;;;8743:119;8777:36;;;;;;;;:::i;:::-;8827:24;8743:119;;;18991:500:24;582:29526:25;;;19454:26:24;;;;19008:414;19393:28;;-1:-1:-1;19008:414:24;;;;:380;582:29526:25;;8320:23;;582:29526;19374:13:24;;;-1:-1:-1;19008:380:24;;19026:329;19127:34;;;;:73;:34;19164:36;19127:34;;19254;19127;;;:::i;:::-;582:29526:25;23608:26;582:29526;;23608:26;582:29526;19164:36:24;:::i;:::-;19127:73;;:::i;:::-;19254:34;;:::i;:::-;1744:340:10;;;;1590:5:24;1744:340:10;;;;;;;22654:20:25;1590:5:24;19317:16;;582:29526:25;1744:340:10;;;19126:207:24;19026:329;;;;;;;7945:693:25;8388:14;;;-1:-1:-1;;;;;8388:14:25;;1590:5:24;582:29526:25;7355:10;8378:24;;8374:264;582:29526;;;;;;8425:35;;;;8374:264;22849:1;582:29526;8542:26;582:29526;;;7556:26;8542;:::i;:::-;582:29526;;;;;;;;:::i;:::-;5800:30:27;582:29526:25;5845:13:27;;;22849:1:25;;;5840:240:27;582:29526:25;;;;6245:22:27;22654:20:25;6245:22:27;;582:29526:25;6285:22:27;-1:-1:-1;;;;;582:29526:25;6285:22:27;;1590:5:24;582:29526:25;6325:19:27;582:29526:25;6325:19:27;;582:29526:25;;22654:20;582:29526;;;;6362:28:27;23423:15:25;6408:31:27;;582:29526:25;6457:22:27;23441:14:25;6457:22:27;;582:29526:25;6497:30:27;6545:24;582:29526:25;6497:30:27;;582:29526:25;6545:24:27;;582:29526:25;;;;6119:464:27;2678:66;22654:20:25;6119:464:27;;582:29526:25;;2678:66:27;;582:29526:25;;2678:66:27;;582:29526:25;5864:31:27;2678:66;;582:29526:25;23423:15;2678:66:27;;582:29526:25;23441:14;2678:66:27;;582:29526:25;;2678:66:27;;582:29526:25;6545:24:27;2678:66;;582:29526:25;;2678:66:27;;582:29526:25;2678:66:27;;;;;582:29526:25;6119:464:27;;582:29526:25;2678:66:27;582:29526:25;;;;2678:66:27;582:29526:25;;;;;;;;;2678:66:27;8598:28:25;582:29526;;;;8598:28;;582:29526;22654:20;582:29526;;6119:464:27;;6096:497;8598:28:25;;:::i;:::-;7945:693;;582:29526;;;-1:-1:-1;;;582:29526:25;;;;;;5845:13:27;5864:31;;;;;;582:29526:25;;5860:42:27;;;;;5975:34;5939:71;582:29526:25;5975:34:27;;;;;:::i;:::-;582:29526:25;;;;;;;;;;5939:71:27;;22654:20:25;5939:71:27;;;582:29526:25;;;:::i;:::-;;;;22654:20;582:29526;;;5939:71:27;22654:20:25;5939:71:27;;;;;;;:::i;:::-;582:29526:25;;5845:13:27;;;;5860:42;;;;;;;7787:77:25;582:29526;;;7833:20;;;;22650:171;22744:66;582:29526;;;22744:23;582:29526;;22744:23;582:29526;22744:66;:::i;:::-;22650:171;;;;22654:55;582:29526;22682:23;582:29526;;22682:23;582:29526;22682:27;;22654:55;;582:29526;;;;;;-1:-1:-1;;582:29526:25;;;;;19286:45;582:29526;;;:::i;:::-;-1:-1:-1;;;;;582:29526:25;756:44:7;582:29526:25;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;582:29526:25;;-1:-1:-1;;19211:59:25;582:29526;;;19211:59;582:29526;;;;;;19286:45;582:29526;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;-1:-1:-1;;;;;582:29526:25;;:::i;:::-;;;;3144:64:24;582:29526:25;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;7310:45:9;582:29526:25;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;1761:18:24;582:29526:25;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;1761:18:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;582:29526:25;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;1761:18:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;15068:21:24;582:29526:25;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;-1:-1:-1;;582:29526:25;;;;:::o;:::-;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;582:29526:25;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;582:29526:25;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;582:29526:25;;;;;;:::o;:::-;;;-1:-1:-1;;;;;582:29526:25;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;:::o;:::-;;;;-1:-1:-1;;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;-1:-1:-1;;582:29526:25;;;;:::o;:::-;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;6863:170:24:-;-1:-1:-1;6938:13:24;6955:16;6938:33;6955:16;;6974:24;;6863:170;:::o;6938:88::-;582:29526:25;;19858:4:24;582:29526:25;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;19842:22:24;;582:29526:25;;;19701:271:24;;;582:29526:25;19729:95:24;582:29526:25;;;;;;19882:14:24;582:29526:25;;;;6938:13:24;582:29526:25;;;;19953:4:24;582:29526:25;;;;;19701:271:24;;;;;:::i;:::-;582:29526:25;19678:304:24;;6863:170;:::o;582:29526:25:-;;;;;19858:4:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;582:29526:25;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;19858:4:24;582:29526:25;;;;;;;;;;;;;;;:::i;:::-;18619:8:24;582:29526:25;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;14515:12;582:29526;-1:-1:-1;;;;;582:29526:25;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;-1:-1:-1;;;582:29526:25;;;;;;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;:::o;:::-;;-1:-1:-1;;;;;582:29526:25;;;;;;;:::o;215:156:22:-;-1:-1:-1;;;;;582:29526:25;291:22:22;287:78;;215:156::o;287:78::-;336:18;582:29526:25;;336:18:22;;;;582:29526:25;;;;;;;:::i;:::-;;;;-1:-1:-1;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;:::o;:::-;;;;-1:-1:-1;;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;;;-1:-1:-1;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;7809:1;582:29526;;;-1:-1:-1;582:29526:25;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::o;1590:5:24:-;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;582:29526:25;;;;;;;:::i;:::-;1590:5:24;;;;;;;;;;;;;;;;-1:-1:-1;1590:5:24;;582:29526:25;;;;1590:5:24:o;2096:672:10:-;;582:29526:25;2274:488:10;;-1:-1:-1;;2274:488:10;;;;;;;;;;;;;;;;;;;;;2096:672;:::o;:::-;;1590:5:24;2274:488:10;;-1:-1:-1;;2274:488:10;;;;;;;;;;;;;;;;;;;;;2096:672;:::o;1328:1782:12:-;;-1:-1:-1;1328:1782:12;;1532:1521;1328:1782;1532:1521;1328:1782;1532:1521;;;-1:-1:-1;;;1532:1521:12;;-1:-1:-1;;;;;1532:1521:12;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1532:1521:12;;;;;582:29526:25;;;1328:1782:12:o;582:29526:25:-;1532:1521:12;;582:29526:25;-1:-1:-1;;;582:29526:25;;1532:1521:12;;582:29526:25;;;;1532:1521:12;582:29526:25;;;;1532:1521:12;582:29526:25;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;:::o;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;-1:-1:-1;582:29526:25;;;;;;;;;;;:::o;16489:1042:24:-;16606:31;;;582:29526:25;;16651:36:24;;;;16707:24;;16703:96;;16489:1042::o;16703:96::-;16758:26;582:29526:25;;16758:26:24;;;;16647:878;16855:21;;;;;;;;;;:::i;:::-;16901:20;;;;:37;;;16647:878;16897:236;;;16965:26;582:29526:25;;16965:26:24;;;;16897:236;582:29526:25;17017:20:24;;17016:76;;;16897:236;17012:121;;16897:236;;;;;-1:-1:-1;582:29526:25;;;17146:369:24;17166:19;;;;;;16647:878;;;;;;;;16489:1042::o;17151:13::-;17244:21;;;;:::i;:::-;582:29526:25;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;:::i;:::-;;-1:-1:-1;;;;;1590:5:24;;;582:29526:25;17388:23:24;;;;;;17286:143;;;;;;582:29526:25;;;-1:-1:-1;;582:29526:25;;;;;;;17286:143:24;1590:5;17286:143;;582:29526:25;;;;17286:143:24;;;;;582:29526:25;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;1590:5:24;582:29526:25;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;582:29526:25;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;582:29526:25;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;:::i;:::-;17286:143:24;;;;;;;;;;;582:29526:25;17286:143:24;;;;582:29526:25;;;17151:13:24;;;;;;;;17286:143;;;;:::i;:::-;;;;;582:29526:25;1590:5:24;-1:-1:-1;1590:5:24;;;;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;17012:121:24;17112:7;;;;;:::o;17016:76::-;17043:21;;;;;:::i;:::-;582:29526:25;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;-1:-1:-1;;;;;582:29526:25;17043:34:24;582:29526:25;;17043:34:24;:::i;:::-;582:29526:25;17043:48:24;17016:76;;16901:37;16925:13;;;;16901:37;;582:29526:25;;;;;;;;;;;;;;;;;;:::o;17652:566:24:-;;;;;3515:233:4;17780:18:24;;:::i;:::-;3515:233:4;;;;;;;;;;;;;;;;;17836:19:24;;;:23;;17832:380;17836:19;;;582:29526:25;17879:57:24;582:29526:25;;;-1:-1:-1;;;;;582:29526:25;;1590:5:24;;;;;;;17879:57;;;;;;;582:29526:25;;;;;;;;;;;:::i;:::-;17879:57:24;582:29526:25;;17879:57:24;;;;;;;;582:29526:25;17879:57:24;-1:-1:-1;17879:57:24;;;17832:380;582:29526:25;;17879:76:24;17875:145;;17832:380;17652:566::o;17875:145::-;17879:57;582:29526:25;;17982:23:24;;;;17879:57;;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;582:29526:25;;1590:5:24;-1:-1:-1;1590:5:24;;;;;17832:380;1590:5;;3915:8:3;1590:5:24;3859:27:3;1590:5:24;;;;;:::i;:::-;3859:27:3;;:::i;:::-;3915:8;;;;;:::i;:::-;-1:-1:-1;;;;;582:29526:25;;;;;18117:20:24;18113:89;;17832:380;17652:566::o;2129:766:3:-;582:29526:25;;;2129:766:3;2276:2;2256:22;;2276:2;;2739:25;2539:180;;;;;;;;;;;;;;;-1:-1:-1;2539:180:3;2739:25;;:::i;:::-;2732:32;;;;;:::o;2252:637::-;2795:83;;2811:1;2795:83;2815:35;2795:83;;:::o;5140:1530::-;;;6199:66;6186:79;;6182:164;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;6457:24:3;;;;;;;;;;;;;;-1:-1:-1;;;;;582:29526:25;;6495:20:3;6491:113;;6614:49;;5140:1530;:::o;6491:113::-;6531:62;;;6457:24;6531:62;;:::o;6457:24::-;582:29526:25;;1590:5:24;;;;;;;;6182:164:3;6281:54;;;6297:1;6281:54;6301:30;6281:54;;:::o;7196:532::-;582:29526:25;;;;;;7282:29:3;;;7327:7;;:::o;7278:444::-;582:29526:25;7378:38:3;;582:29526:25;;;;;7439:23:3;;;;7374:348;7492:35;7483:44;;7492:35;;582:29526:25;;;;7550:46:3;;;;582:29526:25;7550:46:3;;582:29526:25;7550:46:3;7479:243;7626:30;7617:39;7613:109;;7479:243;7196:532::o;7613:109::-;582:29526:25;;;;7679:32:3;;;;582:29526:25;7679:32:3;;582:29526:25;7679:32:3;582:29526:25;-1:-1:-1;;;7291:20:3;582:29526:25;;;;;7291:20:3;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;:::o;:::-;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;:::o;24827:1508::-;;;;;;;582:29526;25161:19;582:29526;25230:23;;25318:17;30010:89;25318:17;;;582:29526;30029:70;30030:31;582:29526;;30030:31;;;:::i;30010:89::-;25375:15;25360:30;;25356:111;;25484:23;;;;582:29526;25484:42;582:29526;;;25484:42;:::i;:::-;582:29526;;;25579:14;25553:40;25579:14;;;582:29526;;;25553:40;;:::i;:::-;25607:10;;;25603:76;;582:29526;1440:19:28;25791:20:25;582:29526;25740:35;1423:69:28;582:29526:25;;;;25375:15;25740:35;:::i;1423:69:28:-;25791:20:25;;582:29526;25791:24;25787:131;;24827:1508;25958:23;;;;582:29526;;;;;1744:340:10;;;-1:-1:-1;;1744:340:10;;;;;;;;;;;;;;;;;;;;;;26038:20:25;26034:126;;24827:1508;26170:38;;26286:41;26170:38;26286:23;26170:38;26286:41;26170:38;;;;25791:20;26170:38;;:::i;:::-;582:29526;;-1:-1:-1;;;;;26270:14:25;;;;1590:5:24;582:29526:25;26286:23;;:::i;:41::-;582:29526;;26286:41;:::i;:::-;24827:1508::o;26034:126::-;25791:20;26074:46;;;26286:23;26170:38;26074:46;26286:41;26074:46;;26286:41;26074:46;;;;;;;:::i;:::-;582:29526;;;26034:126;;;;;;;;;;;;25787:131;25849:58;;;;;;:::i;:::-;25787:131;;;;25603:76;-1:-1:-1;25633:35:25;;-1:-1:-1;25633:35:25;;-1:-1:-1;25647:1:25;;-1:-1:-1;25633:35:25;;-1:-1:-1;;;;;;;25633:35:25:o;26341:299::-;26429:12;;;:::i;:::-;582:29526;-1:-1:-1;582:29526:25;26445:6;582:29526;;;-1:-1:-1;582:29526:25;;26429:31;26425:94;;26532:15;;26550:14;26532:15;:32;:15;;582:29526;26550:14;;582:29526;26532:32;;:::i;:::-;26567:15;-1:-1:-1;26528:106:25;;26341:299::o;26528:106::-;26605:18;582:29526;;26605:18;;;;26646:917;;26809:35;;;582:29526;26809:40;26808:129;;;;;26646:917;26791:221;;;27043:34;;;582:29526;27025:15;;:52;27021:156;;582:29526;27274:26;;582:29526;;27274:26;;;:::i;:::-;-1:-1:-1;;;;;582:29526:25;;-1:-1:-1;;582:29526:25;;;27327:29;582:29526;;27274:26;582:29526;;;;;;;;27274:26;582:29526;;;;27327:130;;;;;26646:917;27310:247;;;;;26646:917;;:::o;27310:247::-;27274:26;582:29526;27489:57;;;-1:-1:-1;;;;;582:29526:25;;;;27489:57;;;582:29526;;;;;;;;;;10231:89:24;27327:130:25;27274:26;582:29526;;;;;27401:29;582:29526;;;;27401:56;;27327:130;;;;;27021:156;582:29526;;;;27100:66;;;;;;;582:29526;27100:66;26808:129;26871:12;;;;;582:29526;26894:35;;;;;;:::i;:::-;26871:65;;;;;26808:129;;;27569:506;;582:29526;;:::i;:::-;;-1:-1:-1;;;;;27830:26:25;;;1590:5:24;582:29526:25;27887:35;28031:26;27887:35;;;582:29526;28031:26;;582:29526;;27830:26;582:29526;;;;;:::i;:::-;;;27772:296;;;582:29526;27830:26;27772:296;;582:29526;27772:296;;;582:29526;27994:15;27772:296;;;582:29526;27887:35;27772:296;;582:29526;27569:506;:::o;28081:175::-;28237:11;582:29526;;;;;;;;;28185:64;582:29526;;;28185:64;;:::i;28568:332::-;;28752:20;;;582:29526;28752:24;;:36;;;28568:332;28748:146;;28568:332;;;;;:::o;28748:146::-;28878:4;582:29526;-1:-1:-1;;;;;1590:5:24;;;582:29526:25;;;28878:4;:::i;:::-;28748:146;;;;;;28752:36;28780:8;;;;28752:36;;582:29526;;;1590:5:24;;;582:29526:25;;;;;:::o;:::-;971:12:26;582:29526:25;1590:5:24;;;582:29526:25;;;;;:::o;1984:626:26:-;582:29526:25;;3446:25:26;2176:65;;-1:-1:-1;2299:10:26;;582:29526:25;;2268:30:26;582:29526:25;;;;;;;;2267:43:26;582:29526:25;;;2267:208:26;;1984:626;2250:304;;;;;582:29526:25;1590:5:24;2299:10:26;;582:29526:25;;2570:14:26;582:29526:25;;;;1590:5:24;1984:626:26;:::o;2250:304::-;2507:36;582:29526:25;;2507:36:26;;;;2267:208;582:29526:25;;;;;;;;;1590:5:24;;;;;582:29526:25;1590:5:24;2330:76:26;;;;582:29526:25;2330:76:26;;;582:29526:25;;;;;;:::i;:::-;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;:::i;:::-;2330:76:26;2299:10;;;2330:76;;;;;;;;582:29526:25;2330:76:26;;;;;2267:208;582:29526:25;;2330:145:26;;2267:208;;;;;2330:76;;;;582:29526:25;2330:76:26;;;;;;;;;:::i;:::-;;;;2176:65;2222:8;;;-1:-1:-1;2222:8:26;:::o;2765:581::-;;;1590:5:24;;;;;:::i;:::-;582:29526:25;3446:25:26;2929:65;;-1:-1:-1;3052:10:26;;582:29526:25;;3021:30:26;582:29526:25;;;;;;;;;;3020:43:26;582:29526:25;;;3020:190:26;;2765:581;3003:286;;;;;3052:10;582:29526:25;;3305:14:26;582:29526:25;;;;;;2765:581:26;:::o;3003:286::-;3242:36;582:29526:25;;3242:36:26;;;;3020:190;582:29526:25;;;;;;;;;1590:5:24;;;;;582:29526:25;1590:5:24;3083:64:26;;;;;;;;582:29526:25;;;;;;:::i;:::-;;-1:-1:-1;;582:29526:25;;;;;;;;;:::i;:::-;3083:64:26;3052:10;;;3083:64;;;;;;;;582:29526:25;3083:64:26;;;;;3020:190;582:29526:25;;3083:127:26;;3020:190;;;;;3083:64;;;;582:29526:25;3083:64:26;;;;;;;;;:::i;:::-;;;;;582:29526:25;;1590:5:24;582:29526:25;;1590:5:24;;;;3075:1011:27;-1:-1:-1;;3075:1011:27;582:29526:25;3215:13:27;3234:21;;;;582:29526:25;;3230:32:27;;;;;3350:24;;;;:::i;:::-;;-1:-1:-1;;;;;1590:5:24;;582:29526:25;7757:20:27;;;;;582:29526:25;;;;;7747:31:27;582:29526:25;;;;;7697:82:27;;;;582:29526:25;351:66:27;582:29526:25;;351:66:27;;;582:29526:25;;351:66:27;;582:29526:25;;7697:82:27;;351:66;582:29526:25;;;;;;;;;;;;;;3299:77:27;582:29526:25;;;;;;7687:93:27;;3299:77;582:29526:25;;;;3299:77:27;;;;;;582:29526:25;;;:::i;:::-;;;;;;;3299:77:27;;;;;;;;;:::i;:::-;582:29526:25;;3215:13:27;;;3230:32;;;;;582:29526:25;;;-1:-1:-1;;;;;3584:17:27;;7757:20;3584:17;;1590:5:24;582:29526:25;3619:14:27;582:29526:25;3619:14:27;;582:29526:25;3651:19:27;;582:29526:25;3651:19:27;;1590:5:24;582:29526:25;3688:19:27;351:66;3688:19;;582:29526:25;3725:31:27;;3299:77;3725:31;;1590:5:24;582:29526:25;3774:31:27;;;;582:29526:25;3823:27:27;;;;1590:5:24;582:29526:25;3868:26:27;;;;;;582:29526:25;3912:17:27;;;;582:29526:25;3947:25:27;3990:19;3947:25;;;582:29526:25;3990:19:27;;582:29526:25;;7757:20:27;582:29526:25;;;;4027:28:27;582:29526:25;;;;3486:583:27;;;815:66;7757:20;3486:583;;582:29526:25;;815:66:27;;582:29526:25;815:66:27;582:29526:25;351:66:27;815;;582:29526:25;3299:77:27;815:66;;582:29526:25;3774:31:27;815:66;;582:29526:25;3823:27:27;815:66;;582:29526:25;815:66:27;;582:29526:25;3912:17:27;815:66;;582:29526:25;3947:25:27;815:66;;582:29526:25;3990:19:27;815:66;;582:29526:25;3234:21:27;815:66;;582:29526:25;815:66:27;;;582:29526:25;815:66:27;;;;;582:29526:25;3486:583:27;;815:66;582:29526:25;;;;;;;;;;;;;7757:20:27;582:29526:25;;3486:583:27;;3463:616;3075:1011;:::o;4920:758::-;;-1:-1:-1;582:29526:25;5052:13:27;5071:12;;;;582:29526:25;;5067:23:27;;;;;5165:15;5122:60;5165:15;;582:29526:25;5165:15:27;;:::i;:::-;;7371:14;-1:-1:-1;;;;;7371:14:27;;;1590:5:24;582:29526:25;7403:23:27;;;;;582:29526:25;7444:23:27;582:29526:25;7444:23:27;;582:29526:25;7485:17:27;;;;;582:29526:25;7520:14:27;;;;;582:29526:25;;;;7313:235:27;;;;582:29526:25;2326:66:27;582:29526:25;;2326:66:27;;;582:29526:25;;2326:66:27;;582:29526:25;2326:66:27;;582:29526:25;2326:66:27;;582:29526:25;2326:66:27;;;;;582:29526:25;7313:235:27;;;;;:::i;:::-;582:29526:25;7290:268:27;;582:29526:25;;;;;;;;;5122:60:27;;;;;;582:29526:25;;;:::i;:::-;;;;;;;;5122:60:27;;;;;;;;;:::i;:::-;582:29526:25;;5052:13:27;;;5067:23;;;;;;-1:-1:-1;;;;;1590:5:24;;;582:29526:25;5392:26:27;7371:14;5392:26;;582:29526:25;5436:26:27;;7403:23;5436:26;;1590:5:24;582:29526:25;5480:22:27;582:29526:25;5480:22:27;;1590:5:24;582:29526:25;7485:17:27;5520:21;;582:29526:25;5559:15:27;2326:66;7520:14;5559:15;;582:29526:25;5592:14:27;;582:29526:25;;7371:14:27;582:29526:25;;;;5624:23:27;582:29526:25;7403:23:27;582:29526:25;5291:370:27;7371:14;5291:370;;582:29526:25;2074:66:27;582:29526:25;;7403:23:27;2074:66;;582:29526:25;;2074:66:27;;582:29526:25;7485:17:27;2074:66;;582:29526:25;7520:14:27;2074:66;;582:29526:25;2326:66:27;2074;;582:29526:25;5071:12:27;2074:66;;582:29526:25;2074:66:27;;;582:29526:25;2074:66:27;;;;;582:29526:25;5291:370:27;;2074:66;582:29526:25;;;;;;;;;;;7403:23:27;582:29526:25;;5268:403:27;;4920:758;:::o
Swarm Source
ipfs://eba2315c8271639a56006b67768be0c0c6e366fb6841900f2b4b05edcbe1d258
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.