Source Code
Overview
S Balance
S Value
$0.00Latest 25 from a total of 44 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Borrow | 41394523 | 176 days ago | IN | 0 S | 0.04462463 | ||||
| Borrow | 41392943 | 177 days ago | IN | 0 S | 0.04559149 | ||||
| Borrow | 41259195 | 177 days ago | IN | 0 S | 0.04788529 | ||||
| Repay Borrow | 36069383 | 213 days ago | IN | 0 S | 0.02468519 | ||||
| Repay Borrow | 36069304 | 213 days ago | IN | 0 S | 0.02468519 | ||||
| Repay Borrow | 35883701 | 215 days ago | IN | 0 S | 0.02468519 | ||||
| Repay Borrow | 35881817 | 215 days ago | IN | 0 S | 0.0251646 | ||||
| Repay Borrow | 35881641 | 215 days ago | IN | 0 S | 0.02468519 | ||||
| Repay Borrow | 35880511 | 215 days ago | IN | 0 S | 0.02492459 | ||||
| Borrow | 35877195 | 215 days ago | IN | 0 S | 0.04494468 | ||||
| Borrow | 35876763 | 215 days ago | IN | 0 S | 0.04635609 | ||||
| Borrow | 35738949 | 215 days ago | IN | 0 S | 0.04420228 | ||||
| Repay Borrow | 35735890 | 215 days ago | IN | 0 S | 0.02492519 | ||||
| Borrow | 35729769 | 216 days ago | IN | 0 S | 0.04420168 | ||||
| Borrow | 35726044 | 216 days ago | IN | 0 S | 0.04502779 | ||||
| Repay Borrow | 35725854 | 216 days ago | IN | 0 S | 0.02468579 | ||||
| Borrow | 35724047 | 216 days ago | IN | 0 S | 0.04420168 | ||||
| Borrow | 35723341 | 216 days ago | IN | 0 S | 0.04420228 | ||||
| Borrow | 35721957 | 216 days ago | IN | 0 S | 0.06902323 | ||||
| Borrow | 35720753 | 216 days ago | IN | 0 S | 0.04420228 | ||||
| Borrow | 35720646 | 216 days ago | IN | 0 S | 0.04420228 | ||||
| Borrow | 35720302 | 216 days ago | IN | 0 S | 0.04420168 | ||||
| Borrow | 35711297 | 216 days ago | IN | 0 S | 0.04718159 | ||||
| Repay Borrow | 34704990 | 222 days ago | IN | 0 S | 0.02492459 | ||||
| Repay Borrow | 34704874 | 222 days ago | IN | 0 S | 0.0279496 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
CNumaLst
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 100 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.20;
import "./CNumaToken.sol";
import "@openzeppelin/contracts_5.0.2/token/ERC20/utils/SafeERC20.sol";
/**
* @title CNumaLst
* @notice CTokens used with numa vault
* @author
*/
contract CNumaLst is CNumaToken {
bool public transferReservesToVault = false;
constructor(
address underlying_,
ComptrollerInterface comptroller_,
InterestRateModel interestRateModel_,
uint initialExchangeRateMantissa_,
string memory name_,
string memory symbol_,
uint8 decimals_,
uint fullUtilizationRate_,
address payable admin_,
address _vault
)
CNumaToken(
underlying_,
comptroller_,
interestRateModel_,
initialExchangeRateMantissa_,
name_,
symbol_,
decimals_,
fullUtilizationRate_,
admin_,
_vault
)
{}
function allowReservesTransferToVault(bool _allow) external {
require(
msg.sender == admin,
"only admin"
);
transferReservesToVault = _allow;
}
/**
* @notice Returns the current per-block borrow interest rate for this cToken
* @return The borrow interest rate per block, scaled by 1e18
*/
function borrowRatePerBlock() external view override returns (uint) {
// NUMALENDING
// borrow rate is based on lending contract cash & vault available to borrow
uint maxBorrowableAmountFromVault;
if (address(vault) != address(0))
maxBorrowableAmountFromVault = vault.getMaxBorrow(false);
uint currentTimestamp = block.timestamp;
uint timestampPrior = accrualBlockTimestamp;
uint deltaTime = currentTimestamp - timestampPrior;
(uint ratePerBlock, ) = interestRateModel.getBorrowRate(
getCashPrior() + maxBorrowableAmountFromVault,
totalBorrows,
totalReserves,
deltaTime,
fullUtilizationRate
);
return ratePerBlock;
}
/**
* @notice Returns the current per-block supply interest rate for this cToken
* @return The supply interest rate per block, scaled by 1e18
*/
function supplyRatePerBlock() external view override returns (uint) {
// NUMALENDING
// supply rate is based on lending contract cash & vault available to borrow
uint maxBorrowableAmountFromVault;
if (address(vault) != address(0))
maxBorrowableAmountFromVault = vault.getMaxBorrow(false);
uint currentTimestamp = block.timestamp;
uint timestampPrior = accrualBlockTimestamp;
uint deltaTime = currentTimestamp - timestampPrior;
return
interestRateModel.getSupplyRate(
getCashPrior() + maxBorrowableAmountFromVault,
totalBorrows,
totalReserves,
reserveFactorMantissa,
deltaTime,
fullUtilizationRate
);
}
/**
* @notice Applies accrued interest to total borrows and reserves
* @dev This calculates interest accrued from the last checkpointed block
* up to the current block and writes new checkpoint to storage.
*/
function accrueInterest() public virtual override returns (uint) {
/* Remember the initial block number */
uint currentBlockNumber = getBlockNumber();
uint accrualBlockNumberPrior = accrualBlockNumber;
/* Short-circuit accumulating 0 interest */
if (accrualBlockNumberPrior == currentBlockNumber) {
return NO_ERROR;
}
/* Read the previous values out of storage */
// NUMALENDING
// interest rate is based on lending contract cash & vault available to borrow
uint maxBorrowableAmountFromVault;
if (address(vault) != address(0))
maxBorrowableAmountFromVault = vault.getMaxBorrow(false);
uint cashPrior = getCashPrior() + maxBorrowableAmountFromVault;
uint borrowsPrior = totalBorrows;
uint reservesPrior = totalReserves;
uint borrowIndexPrior = borrowIndex;
/* Calculate the current borrow interest rate */
(
uint borrowRateMantissa,
uint newfullUtilizationRate
) = interestRateModel.getBorrowRate(
cashPrior,
borrowsPrior,
reservesPrior,
block.timestamp - accrualBlockTimestamp,
fullUtilizationRate
);
require(
borrowRateMantissa <= borrowRateMaxMantissa,
"borrow rate is absurdly high"
);
/* Calculate the number of blocks elapsed since the last accrual */
uint blockDelta = currentBlockNumber - accrualBlockNumberPrior;
/*
* Calculate the interest accumulated into borrows and reserves and the new index:
* simpleInterestFactor = borrowRate * blockDelta
* interestAccumulated = simpleInterestFactor * totalBorrows
* totalBorrowsNew = interestAccumulated + totalBorrows
* totalReservesNew = interestAccumulated * reserveFactor + totalReserves
* borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex
*/
Exp memory simpleInterestFactor = mul_(
Exp({mantissa: borrowRateMantissa}),
blockDelta
);
uint interestAccumulated = mul_ScalarTruncate(
simpleInterestFactor,
borrowsPrior
);
uint totalBorrowsNew = interestAccumulated + borrowsPrior;
uint totalReservesNew = mul_ScalarTruncateAddUInt(
Exp({mantissa: reserveFactorMantissa}),
interestAccumulated,
reservesPrior
);
uint borrowIndexNew = mul_ScalarTruncateAddUInt(
simpleInterestFactor,
borrowIndexPrior,
borrowIndexPrior
);
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We write the previously calculated values into storage */
accrualBlockNumber = currentBlockNumber;
accrualBlockTimestamp = block.timestamp;
borrowIndex = borrowIndexNew;
totalBorrows = totalBorrowsNew;
totalReserves = totalReservesNew;
if (fullUtilizationRate != newfullUtilizationRate) {
emit UpdateRate(fullUtilizationRate, newfullUtilizationRate);
fullUtilizationRate = newfullUtilizationRate;
}
/* We emit an AccrueInterest event */
emit AccrueInterest(
cashPrior,
interestAccumulated,
borrowIndexNew,
totalBorrowsNew
);
return NO_ERROR;
}
function borrowFreshNoTransfer(
address payable borrower,
uint borrowAmount
) internal virtual override {
/* Fail if borrow not allowed */
uint allowed = comptroller.borrowAllowed(
address(this),
borrower,
borrowAmount
);
if (allowed != 0) {
revert BorrowComptrollerRejection(allowed);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
revert BorrowFreshnessCheck();
}
/* Fail gracefully if protocol has insufficient underlying cash */
uint cashPrior = getCashPrior();
if (cashPrior < borrowAmount) {
// not enough cash in lending contract, check if we can get some from the vault
// NUMALENDING
//
if (address(vault) != address(0)) {
uint amountNeeded = borrowAmount - cashPrior;
uint maxBorrowableAmountFromVault = vault.getMaxBorrow(true);
if (amountNeeded <= maxBorrowableAmountFromVault) {
// if ok, borrow from vault
vault.borrow(amountNeeded);
} else {
// not enough in vault
revert BorrowCashNotAvailable();
}
} else {
revert BorrowCashNotAvailable();
}
}
/*
* We calculate the new borrower and total borrow balances, failing on overflow:
* accountBorrowNew = accountBorrow + borrowAmount
* totalBorrowsNew = totalBorrows + borrowAmount
*/
uint accountBorrowsPrev = borrowBalanceStoredInternal(borrower);
uint accountBorrowsNew = accountBorrowsPrev + borrowAmount;
uint totalBorrowsNew = totalBorrows + borrowAmount;
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We write the previously calculated values into storage.
* Note: Avoid token reentrancy attacks by writing increased borrow before external transfer.
`*/
accountBorrows[borrower].principal = accountBorrowsNew;
accountBorrows[borrower].interestIndex = borrowIndex;
totalBorrows = totalBorrowsNew;
/* We emit a Borrow event */
emit Borrow(borrower, borrowAmount, accountBorrowsNew, totalBorrowsNew);
}
function _reduceReservesToVault() internal returns (uint) {
// if (getcashprior > totalReserves) --> we can send totalReserves
// if (getcashprior < totalReserves) --> we can reduce from getcashprior
uint cashPrior = getCashPrior();
uint reduceAmount = totalReserves;
if (cashPrior < reduceAmount) {
reduceAmount = cashPrior;
}
// totalReserves - reduceAmount
uint totalReservesNew;
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
totalReservesNew = totalReserves - reduceAmount;
// Store reserves[n+1] = reserves[n] - reduceAmount
totalReserves = totalReservesNew;
// doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
doTransferOut(payable(address(vault)), reduceAmount);
emit ReservesReduced(address(vault), reduceAmount, totalReservesNew);
return NO_ERROR;
}
/**
* @notice Borrows are repaid by another user (possibly the borrower).
* @param payer the account paying off the borrow
* @param borrower the account with the debt being payed off
* @param repayAmount the amount of underlying tokens being returned, or -1 for the full outstanding amount
* @return (uint) the actual repayment amount.
*/
function repayBorrowFresh(
address payer,
address borrower,
uint repayAmount
) internal override returns (uint) {
uint actualRepayAmount = CToken.repayBorrowFresh(
payer,
borrower,
repayAmount
);
// NUMALENDING
// if we have debt from the vault, repay vault first
uint vaultDebt = vault.getDebt();
if (vaultDebt > 0) {
uint repayToVault = vaultDebt;
if (actualRepayAmount <= vaultDebt) {
repayToVault = actualRepayAmount;
}
// repay vault debt
EIP20Interface(underlying).approve(address(vault), repayToVault);
vault.repay(repayToVault);
}
if (transferReservesToVault)
_reduceReservesToVault();
return actualRepayAmount;
}
/**
* @notice User redeems cTokens in exchange for the underlying asset
* @dev Assumes interest has already been accrued up to the current block
* @param redeemer The address of the account which is redeeming the tokens
* @param redeemTokensIn The number of cTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero)
* @param redeemAmountIn The number of underlying tokens to receive from redeeming cTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero)
*/
function redeemFresh(
address payable redeemer,
uint redeemTokensIn,
uint redeemAmountIn
) internal override {
require(
redeemTokensIn == 0 || redeemAmountIn == 0,
"one of redeemTokensIn or redeemAmountIn must be zero"
);
/* exchangeRate = invoke Exchange Rate Stored() */
Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal()});
uint redeemTokens;
uint redeemAmount;
/* If redeemTokensIn > 0: */
if (redeemTokensIn > 0) {
/*
* We calculate the exchange rate and the amount of underlying to be redeemed:
* redeemTokens = redeemTokensIn
* redeemAmount = redeemTokensIn x exchangeRateCurrent
*/
redeemTokens = redeemTokensIn;
redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokensIn);
} else {
/*
* We get the current exchange rate and calculate the amount to be redeemed:
* redeemTokens = redeemAmountIn / exchangeRate
* redeemAmount = redeemAmountIn
*/
redeemTokens = div_(redeemAmountIn, exchangeRate);
redeemAmount = redeemAmountIn;
// // NUMALENDING
// // this was not in original compound code but I think it's necessary
// // because due to exchange rate we might ask for more underlying tokens than equvalent in cTokens
// // for example X + 500 000 000 wei is equivalent to X
// redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokens);
}
/* Fail if redeem not allowed */
uint allowed = comptroller.redeemAllowed(
address(this),
redeemer,
redeemTokens
);
if (allowed != 0) {
revert RedeemComptrollerRejection(allowed);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
revert RedeemFreshnessCheck();
}
if (getCashPrior() < redeemAmount) {
// NUMALENDING
// try to redeem from vault
uint amountNeeded = redeemAmount - getCashPrior();
uint maxBorrowableAmountFromVault;
if (address(vault) != address(0))
maxBorrowableAmountFromVault = vault.getMaxBorrow(true);
if (amountNeeded <= maxBorrowableAmountFromVault) {
// if ok, borrow from vault
vault.borrow(amountNeeded);
} else {
revert RedeemTransferOutNotPossible();
}
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We write the previously calculated values into storage.
* Note: Avoid token reentrancy attacks by writing reduced supply before external transfer.
*/
totalSupply = totalSupply - redeemTokens;
accountTokens[redeemer] = accountTokens[redeemer] - redeemTokens;
/*
* We invoke doTransferOut for the redeemer and the redeemAmount.
* Note: The cToken must handle variations between ERC-20 and ETH underlying.
* On success, the cToken has redeemAmount less of cash.
* doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
*/
doTransferOut(redeemer, redeemAmount);
/* We emit a Transfer event, and a Redeem event */
emit Transfer(redeemer, address(this), redeemTokens);
emit Redeem(redeemer, redeemAmount, redeemTokens);
/* We call the defense hook */
comptroller.redeemVerify(
address(this),
redeemer,
redeemAmount,
redeemTokens
);
}
/**
* @notice Calculates the exchange rate from the underlying to the CToken
* @dev This function does not accrue interest before calculating the exchange rate
* @return calculated exchange rate scaled by 1e18
*/
function exchangeRateStoredInternal()
internal
view
override
returns (uint)
{
uint _totalSupply = totalSupply;
if (_totalSupply == 0) {
/*
* If there are no tokens minted:
* exchangeRate = initialExchangeRate
*/
return initialExchangeRateMantissa;
} else {
/*
* Otherwise:
* exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply
*/
uint totalCash = getCashPrior();
// NUMALENDING
// vault debt does not count for exchange rate
uint vaultDebt = vault.getDebt();
uint cashPlusBorrowsMinusReserves = totalCash +
totalBorrows -
vaultDebt -
totalReserves;
uint exchangeRate = (cashPlusBorrowsMinusReserves * expScale) /
_totalSupply;
return exchangeRate;
}
}
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.20;
import "./CErc20Immutable.sol";
import "@openzeppelin/contracts_5.0.2/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts_5.0.2/utils/structs/EnumerableSet.sol";
import "../interfaces/INumaVault.sol";
import "./INumaLeverageStrategy.sol";
/**
* @title CNumaToken
* @notice CTokens used with numa vault
* @author
*/
contract CNumaToken is CErc20Immutable {
INumaVault public vault;
uint constant max_strategy = 10;
using EnumerableSet for EnumerableSet.AddressSet;
EnumerableSet.AddressSet leverageStrategies;
/// @notice set vault event
event SetVault(address vaultAddress);
/// @notice open leverage event
event LeverageOpen(
CNumaToken indexed _collateral,
uint _suppliedAmount,
uint _borrowAmountVault,
uint _borrowAmount
);
/// @notice close leverage event
event LeverageClose(CNumaToken indexed _collateral, uint _borrowtorepay);
event RemovedStrategy(address);
event AddedStrategy(address);
modifier onlyAdmin() {
require(msg.sender == admin, "only admin");
_;
}
constructor(
address underlying_,
ComptrollerInterface comptroller_,
InterestRateModel interestRateModel_,
uint initialExchangeRateMantissa_,
string memory name_,
string memory symbol_,
uint8 decimals_,
uint fullUtilizationRate_,
address payable admin_,
address _vault
)
CErc20Immutable(
underlying_,
comptroller_,
interestRateModel_,
initialExchangeRateMantissa_,
name_,
symbol_,
decimals_,
fullUtilizationRate_,
admin_
)
{
vault = INumaVault(_vault);
}
function setVault(address _vault) external onlyAdmin {
vault = INumaVault(_vault);
emit SetVault(_vault);
}
/**
* @dev returns vaults list
*/
function getLeverageStrategies() external view returns (address[] memory) {
return leverageStrategies.values();
}
/**
* @dev adds a leverage strategy
*/
function addStrategy(address _strategy) external onlyAdmin {
require(
leverageStrategies.length() < max_strategy,
"too many strategies"
);
require(leverageStrategies.add(_strategy), "already in list");
emit AddedStrategy(_strategy);
}
/**
* @dev removes a leverage strategy
*/
function removeStrategy(address _strategy) external onlyAdmin {
require(leverageStrategies.contains(_strategy), "not in list");
leverageStrategies.remove(_strategy);
emit RemovedStrategy(_strategy);
}
function isStrategy(address _addy) public view returns (bool) {
return (leverageStrategies.contains(_addy));
}
function borrowInternalNoTransfer(
uint borrowAmount,
address borrower
) internal nonReentrant {
accrueInterest();
// borrowFresh emits borrow-specific logs on errors, so we don't need to
borrowFreshNoTransfer(payable(borrower), borrowAmount);
}
function getAmountIn(
uint256 _amount,
bool _closePosition,
uint _strategyIndex
) external view returns (uint256) {
INumaLeverageStrategy strat = INumaLeverageStrategy(
leverageStrategies.at(_strategyIndex)
);
return strat.getAmountIn(_amount, _closePosition);
}
/**
* @notice leverage by depositing and borrowing
* LTV will be _borrowAmount/(_borrowAmount+_suppliedAmount)
* 1) flash borrow _collateral.underlying from vault (will be repaid at the end of the function)
* 2) deposit as collateral (mint input CNumaToken), send minted Ctoken to sender
* 3) borrow other token using collateral
* 4) convert to other token using vault
* 5) flash repay vault
*
*/
function leverageStrategy(
uint _suppliedAmount,
uint _borrowAmount,
uint _maxBorrowAmount,
CNumaToken _collateral,
uint _strategyIndex
) external {
// Sherlock-issue 120
require(
(
((address(this) == vault.getcLstAddress()) && (address(_collateral) == vault.getcNumaAddress()))
||
((address(this) == vault.getcNumaAddress()) && (address(_collateral) == vault.getcLstAddress()))
)
, "invalid collateral");
// AUDITV2FIX if we don't do that, borrow balance might change when calling borrowinternal
accrueInterest();
_collateral.accrueInterest();
INumaLeverageStrategy strat = INumaLeverageStrategy(
leverageStrategies.at(_strategyIndex)
);
address underlyingCollateral = _collateral.underlying();
// borrow from vault
vault.borrowLeverage(_borrowAmount, false);
// get user tokens
SafeERC20.safeTransferFrom(
IERC20(underlyingCollateral),
msg.sender,
address(this),
_suppliedAmount
);
uint totalAmount = _suppliedAmount + _borrowAmount;
// supply (mint collateral)
uint balCtokenBefore = EIP20Interface(address(_collateral)).balanceOf(
address(this)
);
EIP20Interface(underlyingCollateral).approve(
address(_collateral),
totalAmount
);
_collateral.mint(totalAmount);
uint balCtokenAfter = EIP20Interface(address(_collateral)).balanceOf(
address(this)
);
// send collateral to sender
//uint receivedtokens = balCtokenAfter - balCtokenBefore;
require((balCtokenAfter - balCtokenBefore) > 0, "no collateral");
// transfer collateral to sender
SafeERC20.safeTransfer(
IERC20(address(_collateral)),
msg.sender,
(balCtokenAfter - balCtokenBefore)
);
// how much to we need to borrow to repay vault
uint borrowAmount = strat.getAmountIn(_borrowAmount, false);
// sherlock issue-182
require(borrowAmount <= _maxBorrowAmount);
// Sherlock-issue 120
//uint accountBorrowBefore = accountBorrows[msg.sender].principal;
uint accountBorrowBefore = borrowBalanceStored(msg.sender);
// borrow but do not transfer borrowed tokens
borrowInternalNoTransfer(borrowAmount, msg.sender);
//
require(
(accountBorrows[msg.sender].principal - accountBorrowBefore) ==
borrowAmount,
"borrow ko"
);
// swap
EIP20Interface(underlying).approve(address(strat), borrowAmount);
(uint collateralReceived, uint unUsedInput) = strat.swap(
borrowAmount,
_borrowAmount,
false
);
// repay flashloan
EIP20Interface(underlyingCollateral).approve(
address(vault),
_borrowAmount
);
vault.repayLeverage(false);
//refund if more collateral is received than needed
if (collateralReceived > _borrowAmount) {
// send back the surplus
SafeERC20.safeTransfer(
IERC20(underlyingCollateral),
msg.sender,
collateralReceived - _borrowAmount
);
}
if (unUsedInput > 0) {
// we did not use all that was borrowed
// so we can repay that borrow
repayBorrowFresh(address(this), msg.sender, unUsedInput);
}
emit LeverageOpen(
_collateral,
_suppliedAmount,
_borrowAmount,
borrowAmount
);
}
function closeLeverageAmount(
CNumaToken _collateral,
uint _borrowtorepay,
uint _strategyIndex
) public view returns (uint, uint) {
INumaLeverageStrategy strat = INumaLeverageStrategy(
leverageStrategies.at(_strategyIndex)
);
// amount of underlying needed
uint swapAmountIn = strat.getAmountIn(_borrowtorepay, true);
// amount of ctokens to redeem this amount
Exp memory exchangeRate = Exp({
mantissa: _collateral.exchangeRateStored()
});
uint cTokenAmount = div_(swapAmountIn, exchangeRate);
return (cTokenAmount, swapAmountIn);
}
function closeLeverageStrategy(
CNumaToken _collateral,
uint _borrowtorepay,
uint _maxRedeemedAmount,
uint _strategyIndex
) external {
// Sherlock-issue 120
require(
(
((address(this) == vault.getcLstAddress()) && (address(_collateral) == vault.getcNumaAddress()))
||
((address(this) == vault.getcNumaAddress()) && (address(_collateral) == vault.getcLstAddress()))
)
, "invalid collateral");
// AUDITV2FIX
accrueInterest();
_collateral.accrueInterest();
INumaLeverageStrategy strat = INumaLeverageStrategy(
leverageStrategies.at(_strategyIndex)
);
address underlyingCollateral = _collateral.underlying();
// get borrowed amount
uint borrowAmountFull = borrowBalanceStored(msg.sender);
// clip to borrowed amount
if (_borrowtorepay > borrowAmountFull)
_borrowtorepay = borrowAmountFull;
// flashloan
vault.borrowLeverage(_borrowtorepay, true);
// repay borrow
repayBorrowFresh(address(this), msg.sender, _borrowtorepay);
// amount of underlying needed
(uint cTokenAmount, uint swapAmountIn) = closeLeverageAmount(
_collateral,
_borrowtorepay,
_strategyIndex
);
// sherlock issue-182
//require(swapAmountIn >= _minRedeemedAmount);
require(swapAmountIn <= _maxRedeemedAmount);
SafeERC20.safeTransferFrom(
IERC20(address(_collateral)),
msg.sender,
address(this),
cTokenAmount
);
// redeem to underlying
uint balBefore = IERC20(underlyingCollateral).balanceOf(address(this));
_collateral.redeemUnderlying(swapAmountIn);
uint balAfter = IERC20(underlyingCollateral).balanceOf(address(this));
uint received = balAfter - balBefore;
require(received >= swapAmountIn, "not enough redeem");
// swap to get enough token to repay flashlon
EIP20Interface(underlyingCollateral).approve(
address(strat),
swapAmountIn
);
(uint bought, uint unusedAmount) = strat.swap(
swapAmountIn,
_borrowtorepay,
true
);
// repay FLASHLOAN
EIP20Interface(underlying).approve(address(vault), _borrowtorepay);
vault.repayLeverage(true);
// send what has not been swapped to msg.sender (surplus)
if (bought > _borrowtorepay) {
// send back the surplus
SafeERC20.safeTransfer(
IERC20(underlying),
msg.sender,
bought - _borrowtorepay
);
}
// send also collateral that was not needed
if (unusedAmount > 0) {
SafeERC20.safeTransfer(
IERC20(underlyingCollateral),
msg.sender,
unusedAmount
);
}
emit LeverageClose(_collateral, _borrowtorepay);
}
/**
* @notice The sender liquidates the borrowers collateral.
* The collateral seized is transferred to the liquidator.
* @param borrower The borrower of this cToken to be liquidated
* @param repayAmount The amount of the underlying borrowed asset to repay
* @param cTokenCollateral The market in which to seize collateral from the borrower
* @return (uint error, uint badDebt) The error code and amount of bad debt */
function liquidateBorrow(
address borrower,
uint repayAmount,
CTokenInterface cTokenCollateral
) external override returns (uint,uint) {
// only vault can liquidate
require(msg.sender == address(vault), "vault only");
// sherlock 101 153 returning bad debt so that vault can decide whether to clip
// profit or not
uint badDebt = liquidateBorrowInternal(borrower, repayAmount, cTokenCollateral);
return (NO_ERROR,badDebt);
}
function liquidateBadDebt(
address borrower,
uint repayAmount,
uint percentageToTake,
CTokenInterface cTokenCollateral
) external override returns (uint) {
// only vault can liquidate
require(msg.sender == address(vault), "vault only");
liquidateBadDebtInternal(
borrower,
repayAmount,
percentageToTake,
cTokenCollateral
);
return NO_ERROR;
}
/**
* @notice Users borrow assets from the protocol to their own address
* @param borrowAmount The amount of the underlying asset to borrow
*/
function borrowFreshNoTransfer(
address payable borrower,
uint borrowAmount
) internal virtual override {
/* Fail if borrow not allowed */
uint allowed = comptroller.borrowAllowed(
address(this),
borrower,
borrowAmount
);
if (allowed != 0) {
revert BorrowComptrollerRejection(allowed);
}
// check if vault allows borrows
// borrowing numa is not allowed when CF < CF_SEVERE
// only needed for numa borrows (lst borrows will go through CNumaLst::borrowFreshNoTransfer)
if (address(vault) != address(0)) {
if (!vault.borrowAllowed(address(this)))
{
revert BorrowNotAllowed();
}
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
revert BorrowFreshnessCheck();
}
/* Fail gracefully if protocol has insufficient underlying cash */
if (getCashPrior() < borrowAmount) {
revert BorrowCashNotAvailable();
}
/*
* We calculate the new borrower and total borrow balances, failing on overflow:
* accountBorrowNew = accountBorrow + borrowAmount
* totalBorrowsNew = totalBorrows + borrowAmount
*/
uint accountBorrowsPrev = borrowBalanceStoredInternal(borrower);
uint accountBorrowsNew = accountBorrowsPrev + borrowAmount;
uint totalBorrowsNew = totalBorrows + borrowAmount;
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We write the previously calculated values into storage.
* Note: Avoid token reentrancy attacks by writing increased borrow before external transfer.
`*/
accountBorrows[borrower].principal = accountBorrowsNew;
accountBorrows[borrower].interestIndex = borrowIndex;
totalBorrows = totalBorrowsNew;
/* We emit a Borrow event */
emit Borrow(borrower, borrowAmount, accountBorrowsNew, totalBorrowsNew);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.20;
import "./CErc20.sol";
/**
* @title Compound's CErc20Immutable Contract
* @notice CTokens which wrap an EIP-20 underlying and are immutable
* @author Compound
*/
abstract contract CErc20Immutable is CErc20 {
/**
* @notice Construct a new money market
* @param underlying_ The address of the underlying asset
* @param comptroller_ The address of the Comptroller
* @param interestRateModel_ The address of the interest rate model
* @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
* @param name_ ERC-20 name of this token
* @param symbol_ ERC-20 symbol of this token
* @param decimals_ ERC-20 decimal precision of this token
* @param admin_ Address of the administrator of this token
*/
constructor(
address underlying_,
ComptrollerInterface comptroller_,
InterestRateModel interestRateModel_,
uint initialExchangeRateMantissa_,
string memory name_,
string memory symbol_,
uint8 decimals_,
uint fullUtilizationRate_,
address payable admin_
) {
// Creator of the contract is admin during initialization
admin = payable(msg.sender);
// Initialize the market
initialize(
underlying_,
comptroller_,
interestRateModel_,
initialExchangeRateMantissa_,
name_,
symbol_,
decimals_,
fullUtilizationRate_
);
// Set the proper admin now that initialization is done
admin = admin_;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
interface INumaVault {
function buy(uint, uint, address) external returns (uint);
function sell(uint, uint, address) external returns (uint);
function getDebt() external view returns (uint);
function repay(uint amount) external;
function borrow(uint amount) external;
function getEthBalance() external view returns (uint256);
function getEthBalanceNoDebt() external view returns (uint256);
function getMaxBorrow(bool _useCapParameter) external view returns (uint256);
function numaToLst(uint256 _amount) external view returns (uint256);
function lstToNuma(uint256 _amount) external view returns (uint256);
function repayLeverage(bool _closePosition) external;
function borrowLeverage(uint _amount, bool _closePosition) external;
function updateVault() external;
function getcNumaAddress() external view returns (address);
function getcLstAddress() external view returns (address);
function getMinBorrowAmountAllowPartialLiquidation(address) external view returns (uint);
function borrowAllowed(address _ctokenAddress) external returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
interface INumaLeverageStrategy {
function getAmountIn(
uint256 _amount,
bool _closePos
) external view returns (uint256);
function swap(
uint256 _inputAmount,
uint256 _minAmount,
bool _closePosition
) external returns (uint256, uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.20;
import "./CToken.sol";
interface CompLike {
function delegate(address delegatee) external;
}
/**
* @title Compound's CErc20 Contract
* @notice CTokens which wrap an EIP-20 underlying
* @author Compound
*/
abstract contract CErc20 is CToken, CErc20Interface {
/**
* @notice Initialize the new money market
* @param underlying_ The address of the underlying asset
* @param comptroller_ The address of the Comptroller
* @param interestRateModel_ The address of the interest rate model
* @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
* @param name_ ERC-20 name of this token
* @param symbol_ ERC-20 symbol of this token
* @param decimals_ ERC-20 decimal precision of this token
*/
function initialize(
address underlying_,
ComptrollerInterface comptroller_,
InterestRateModel interestRateModel_,
uint initialExchangeRateMantissa_,
string memory name_,
string memory symbol_,
uint8 decimals_,
uint fullUtilizationRate_
) public {
// CToken initialize does the bulk of the work
super.initialize(
comptroller_,
interestRateModel_,
initialExchangeRateMantissa_,
name_,
symbol_,
decimals_,
fullUtilizationRate_
);
// Set underlying and sanity check it
underlying = underlying_;
EIP20Interface(underlying).totalSupply();
}
/*** User Interface ***/
/**
* @notice Sender supplies assets into the market and receives cTokens in exchange
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param mintAmount The amount of the underlying asset to supply
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function mint(uint mintAmount) external override returns (uint) {
mintInternal(mintAmount);
return NO_ERROR;
}
/**
* @notice Sender redeems cTokens in exchange for the underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param redeemTokens The number of cTokens to redeem into underlying
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function redeem(uint redeemTokens) external override returns (uint) {
redeemInternal(redeemTokens);
return NO_ERROR;
}
/**
* @notice Sender redeems cTokens in exchange for a specified amount of underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param redeemAmount The amount of underlying to redeem
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function redeemUnderlying(
uint redeemAmount
) external override returns (uint) {
redeemUnderlyingInternal(redeemAmount);
return NO_ERROR;
}
/**
* @notice Sender borrows assets from the protocol to their own address
* @param borrowAmount The amount of the underlying asset to borrow
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function borrow(uint borrowAmount) external override returns (uint) {
borrowInternal(borrowAmount);
return NO_ERROR;
}
/**
* @notice Sender repays their own borrow
* @param repayAmount The amount to repay, or -1 for the full outstanding amount
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function repayBorrow(uint repayAmount) external override returns (uint) {
repayBorrowInternal(repayAmount);
return NO_ERROR;
}
/**
* @notice Sender repays a borrow belonging to borrower
* @param borrower the account with the debt being payed off
* @param repayAmount The amount to repay, or -1 for the full outstanding amount
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function repayBorrowBehalf(
address borrower,
uint repayAmount
) external override returns (uint) {
repayBorrowBehalfInternal(borrower, repayAmount);
return NO_ERROR;
}
/**
* @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock)
* @param token The address of the ERC-20 token to sweep
*/
function sweepToken(EIP20NonStandardInterface token) external override {
require(
msg.sender == admin,
"CErc20::sweepToken: only admin can sweep tokens"
);
require(
address(token) != underlying,
"CErc20::sweepToken: can not sweep underlying token"
);
uint256 balance = token.balanceOf(address(this));
token.transfer(admin, balance);
}
/**
* @notice The sender adds to reserves.
* @param addAmount The amount fo underlying token to add as reserves
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _addReserves(uint addAmount) external override returns (uint) {
return _addReservesInternal(addAmount);
}
/*** Safe Token ***/
/**
* @notice Gets balance of this contract in terms of the underlying
* @dev This excludes the value of the current message, if any
* @return The quantity of underlying tokens owned by this contract
*/
function getCashPrior() internal view virtual override returns (uint) {
EIP20Interface token = EIP20Interface(underlying);
return token.balanceOf(address(this));
}
/**
* @dev Similar to EIP20 transfer, except it handles a False result from `transferFrom` and reverts in that case.
* This will revert due to insufficient balance or insufficient allowance.
* This function returns the actual amount received,
* which may be less than `amount` if there is a fee attached to the transfer.
*
* Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
* See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
*/
function doTransferIn(
address from,
uint amount
) internal virtual override returns (uint) {
// Read from storage once
address underlying_ = underlying;
EIP20NonStandardInterface token = EIP20NonStandardInterface(
underlying_
);
uint balanceBefore = EIP20Interface(underlying_).balanceOf(
address(this)
);
token.transferFrom(from, address(this), amount);
bool success;
assembly {
switch returndatasize()
case 0 {
// This is a non-standard ERC-20
success := not(0) // set success to true
}
case 32 {
// This is a compliant ERC-20
returndatacopy(0, 0, 32)
success := mload(0) // Set `success = returndata` of override external call
}
default {
// This is an excessively non-compliant ERC-20, revert.
revert(0, 0)
}
}
require(success, "TOKEN_TRANSFER_IN_FAILED");
// Calculate the amount that was *actually* transferred
uint balanceAfter = EIP20Interface(underlying_).balanceOf(
address(this)
);
return balanceAfter - balanceBefore; // underflow already checked above, just subtract
}
/**
* @dev Similar to EIP20 transfer, except it handles a False success from `transfer` and returns an explanatory
* error code rather than reverting. If caller has not called checked protocol's balance, this may revert due to
* insufficient cash held in this contract. If caller has checked protocol's balance prior to this call, and verified
* it is >= amount, this should not revert in normal conditions.
*
* Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
* See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
*/
function doTransferOut(
address payable to,
uint amount
) internal virtual override {
EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying);
token.transfer(to, amount);
bool success;
assembly {
switch returndatasize()
case 0 {
// This is a non-standard ERC-20
success := not(0) // set success to true
}
case 32 {
// This is a compliant ERC-20
returndatacopy(0, 0, 32)
success := mload(0) // Set `success = returndata` of override external call
}
default {
// This is an excessively non-compliant ERC-20, revert.
revert(0, 0)
}
}
require(success, "TOKEN_TRANSFER_OUT_FAILED");
}
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.20;
import "./ComptrollerInterface.sol";
import "./CTokenInterfaces.sol";
import "./ErrorReporter.sol";
import "./EIP20Interface.sol";
import "./InterestRateModel.sol";
import "./ExponentialNoError.sol";
/**
* @title Compound's CToken Contract
* @notice Abstract base for CTokens
* @author Compound
*/
abstract contract CToken is
CTokenInterface,
ExponentialNoError,
TokenErrorReporter
{
/**
* @notice Initialize the money market
* @param comptroller_ The address of the Comptroller
* @param interestRateModel_ The address of the interest rate model
* @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
* @param name_ EIP-20 name of this token
* @param symbol_ EIP-20 symbol of this token
* @param decimals_ EIP-20 decimal precision of this token
*/
function initialize(
ComptrollerInterface comptroller_,
InterestRateModel interestRateModel_,
uint initialExchangeRateMantissa_,
string memory name_,
string memory symbol_,
uint8 decimals_,
uint fullUtilizationRate_
) public {
require(msg.sender == admin, "only admin may initialize the market");
require(
accrualBlockNumber == 0 && borrowIndex == 0,
"market may only be initialized once"
);
// Set initial exchange rate
initialExchangeRateMantissa = initialExchangeRateMantissa_;
require(
initialExchangeRateMantissa > 0,
"initial exchange rate must be greater than zero."
);
// Set the comptroller
uint err = _setComptroller(comptroller_);
require(err == NO_ERROR, "setting comptroller failed");
// Initialize block number and borrow index (block number mocks depend on comptroller being set)
accrualBlockNumber = getBlockNumber();
borrowIndex = mantissaOne;
accrualBlockTimestamp = block.timestamp;
fullUtilizationRate = fullUtilizationRate_;
// Set the interest rate model (depends on block number / borrow index)
err = _setInterestRateModelFresh(interestRateModel_);
require(err == NO_ERROR, "setting interest rate model failed");
name = name_;
symbol = symbol_;
decimals = decimals_;
// The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund)
_notEntered = true;
}
/**
* @notice Transfer `tokens` tokens from `src` to `dst` by `spender`
* @dev Called by both `transfer` and `transferFrom` internally
* @param spender The address of the account performing the transfer
* @param src The address of the source account
* @param dst The address of the destination account
* @param tokens The number of tokens to transfer
* @return 0 if the transfer succeeded, else revert
*/
function transferTokens(
address spender,
address src,
address dst,
uint tokens
) internal returns (uint) {
// sherlock issue 168
// Before transferring CToken, the accrueInterest() function should be called first
accrueInterest();
/* Fail if transfer not allowed */
uint allowed = comptroller.transferAllowed(
address(this),
src,
dst,
tokens
);
if (allowed != 0) {
revert TransferComptrollerRejection(allowed);
}
/* Do not allow self-transfers */
if (src == dst) {
revert TransferNotAllowed();
}
/* Get the allowance, infinite for the account owner */
uint startingAllowance = 0;
if (spender == src) {
startingAllowance = type(uint).max;
} else {
startingAllowance = transferAllowances[src][spender];
}
/* Do the calculations, checking for {under,over}flow */
uint allowanceNew = startingAllowance - tokens;
uint srcTokensNew = accountTokens[src] - tokens;
uint dstTokensNew = accountTokens[dst] + tokens;
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
accountTokens[src] = srcTokensNew;
accountTokens[dst] = dstTokensNew;
/* Eat some of the allowance (if necessary) */
if (startingAllowance != type(uint).max) {
transferAllowances[src][spender] = allowanceNew;
}
/* We emit a Transfer event */
emit Transfer(src, dst, tokens);
// unused function
// comptroller.transferVerify(address(this), src, dst, tokens);
return NO_ERROR;
}
/**
* @notice Transfer `amount` tokens from `msg.sender` to `dst`
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
* @return Whether or not the transfer succeeded
*/
function transfer(
address dst,
uint256 amount
) external override nonReentrant returns (bool) {
return transferTokens(msg.sender, msg.sender, dst, amount) == NO_ERROR;
}
/**
* @notice Transfer `amount` tokens from `src` to `dst`
* @param src The address of the source account
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
* @return Whether or not the transfer succeeded
*/
function transferFrom(
address src,
address dst,
uint256 amount
) external override nonReentrant returns (bool) {
return transferTokens(msg.sender, src, dst, amount) == NO_ERROR;
}
/**
* @notice Approve `spender` to transfer up to `amount` from `src`
* @dev This will overwrite the approval amount for `spender`
* and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
* @param spender The address of the account which may transfer tokens
* @param amount The number of tokens that are approved (uint256.max means infinite)
* @return Whether or not the approval succeeded
*/
function approve(
address spender,
uint256 amount
) external override returns (bool) {
address src = msg.sender;
transferAllowances[src][spender] = amount;
emit Approval(src, spender, amount);
return true;
}
/**
* @notice Get the current allowance from `owner` for `spender`
* @param owner The address of the account which owns the tokens to be spent
* @param spender The address of the account which may transfer tokens
* @return The number of tokens allowed to be spent (-1 means infinite)
*/
function allowance(
address owner,
address spender
) external view override returns (uint256) {
return transferAllowances[owner][spender];
}
/**
* @notice Get the token balance of the `owner`
* @param owner The address of the account to query
* @return The number of tokens owned by `owner`
*/
function balanceOf(address owner) external view override returns (uint256) {
return accountTokens[owner];
}
/**
* @notice Get the underlying balance of the `owner`
* @dev This also accrues interest in a transaction
* @param owner The address of the account to query
* @return The amount of underlying owned by `owner`
*/
function balanceOfUnderlying(
address owner
) external override returns (uint) {
Exp memory exchangeRate = Exp({mantissa: exchangeRateCurrent()});
return mul_ScalarTruncate(exchangeRate, accountTokens[owner]);
}
/**
* @notice Get a snapshot of the account's balances, and the cached exchange rate
* @dev This is used by comptroller to more efficiently perform liquidity checks.
* @param account Address of the account to snapshot
* @return (possible error, token balance, borrow balance, exchange rate mantissa)
*/
function getAccountSnapshot(
address account
) external view override returns (uint, uint, uint, uint) {
return (
NO_ERROR,
accountTokens[account],
borrowBalanceStoredInternal(account),
exchangeRateStoredInternal()
);
}
/**
* @dev Function to simply retrieve block number
* This exists mainly for inheriting test contracts to stub this result.
*/
function getBlockNumber() internal view virtual returns (uint) {
return block.number;
}
/**
* @notice Returns the current per-block borrow interest rate for this cToken
* @return The borrow interest rate per block, scaled by 1e18
*/
function borrowRatePerBlock()
external
view
virtual
override
returns (uint)
{
uint currentTimestamp = block.timestamp;
uint timestampPrior = accrualBlockTimestamp;
uint deltaTime = currentTimestamp - timestampPrior;
(uint borrowRateMantissa, ) = interestRateModel.getBorrowRate(
getCashPrior(),
totalBorrows,
totalReserves,
deltaTime,
fullUtilizationRate
);
return borrowRateMantissa;
}
/**
* @notice Returns the current per-block supply interest rate for this cToken
* @return The supply interest rate per block, scaled by 1e18
*/
function supplyRatePerBlock()
external
view
virtual
override
returns (uint)
{
uint currentTimestamp = block.timestamp;
uint timestampPrior = accrualBlockTimestamp;
uint deltaTime = currentTimestamp - timestampPrior;
return
interestRateModel.getSupplyRate(
getCashPrior(),
totalBorrows,
totalReserves,
reserveFactorMantissa,
deltaTime,
fullUtilizationRate
);
}
/**
* @notice Returns the current total borrows plus accrued interest
* @return The total borrows with interest
*/
function totalBorrowsCurrent()
external
override
nonReentrant
returns (uint)
{
accrueInterest();
return totalBorrows;
}
/**
* @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex
* @param account The address whose balance should be calculated after updating borrowIndex
* @return The calculated balance
*/
function borrowBalanceCurrent(
address account
) external override nonReentrant returns (uint) {
accrueInterest();
return borrowBalanceStored(account);
}
/**
* @notice Return the borrow balance of account based on stored data
* @param account The address whose balance should be calculated
* @return The calculated balance
*/
function borrowBalanceStored(
address account
) public view override returns (uint) {
return borrowBalanceStoredInternal(account);
}
/**
* @notice Return the borrow balance of account based on stored data
* @param account The address whose balance should be calculated
* @return (error code, the calculated balance or 0 if error code is non-zero)
*/
function borrowBalanceStoredInternal(
address account
) internal view returns (uint) {
/* Get borrowBalance and borrowIndex */
BorrowSnapshot storage borrowSnapshot = accountBorrows[account];
/* If borrowBalance = 0 then borrowIndex is likely also 0.
* Rather than failing the calculation with a division by 0, we immediately return 0 in this case.
*/
if (borrowSnapshot.principal == 0) {
return 0;
}
/* Calculate new borrow balance using the interest index:
* recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex
*/
uint principalTimesIndex = borrowSnapshot.principal * borrowIndex;
return principalTimesIndex / borrowSnapshot.interestIndex;
}
/**
* @notice Accrue interest then return the up-to-date exchange rate
* @return Calculated exchange rate scaled by 1e18
*/
function exchangeRateCurrent() public override nonReentrant returns (uint) {
accrueInterest();
return exchangeRateStored();
}
/**
* @notice Calculates the exchange rate from the underlying to the CToken
* @dev This function does not accrue interest before calculating the exchange rate
* @return Calculated exchange rate scaled by 1e18
*/
function exchangeRateStored() public view override returns (uint) {
return exchangeRateStoredInternal();
}
/**
* @notice Calculates the exchange rate from the underlying to the CToken
* @dev This function does not accrue interest before calculating the exchange rate
* @return calculated exchange rate scaled by 1e18
*/
function exchangeRateStoredInternal() internal view virtual returns (uint) {
uint _totalSupply = totalSupply;
if (_totalSupply == 0) {
/*
* If there are no tokens minted:
* exchangeRate = initialExchangeRate
*/
return initialExchangeRateMantissa;
} else {
/*
* Otherwise:
* exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply
*/
uint totalCash = getCashPrior();
uint cashPlusBorrowsMinusReserves = totalCash +
totalBorrows -
totalReserves;
uint exchangeRate = (cashPlusBorrowsMinusReserves * expScale) /
_totalSupply;
return exchangeRate;
}
}
/**
* @notice Get cash balance of this cToken in the underlying asset
* @return The quantity of underlying asset owned by this contract
*/
function getCash() external view override returns (uint) {
return getCashPrior();
}
/**
* @notice Applies accrued interest to total borrows and reserves
* @dev This calculates interest accrued from the last checkpointed block
* up to the current block and writes new checkpoint to storage.
*/
function accrueInterest() public virtual override returns (uint) {
/* Remember the initial block number */
uint currentBlockNumber = getBlockNumber();
uint accrualBlockNumberPrior = accrualBlockNumber;
/* Short-circuit accumulating 0 interest */
if (accrualBlockNumberPrior == currentBlockNumber) {
return NO_ERROR;
}
/* Read the previous values out of storage */
uint cashPrior = getCashPrior();
uint borrowsPrior = totalBorrows;
uint reservesPrior = totalReserves;
uint borrowIndexPrior = borrowIndex;
/* Calculate the current borrow interest rate */
(
uint borrowRateMantissa,
uint newfullUtilizationRate
) = interestRateModel.getBorrowRate(
cashPrior,
borrowsPrior,
reservesPrior,
block.timestamp - accrualBlockTimestamp,
fullUtilizationRate
);
require(
borrowRateMantissa <= borrowRateMaxMantissa,
"borrow rate is absurdly high"
);
/* Calculate the number of blocks elapsed since the last accrual */
uint blockDelta = currentBlockNumber - accrualBlockNumberPrior;
/*
* Calculate the interest accumulated into borrows and reserves and the new index:
* simpleInterestFactor = borrowRate * blockDelta
* interestAccumulated = simpleInterestFactor * totalBorrows
* totalBorrowsNew = interestAccumulated + totalBorrows
* totalReservesNew = interestAccumulated * reserveFactor + totalReserves
* borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex
*/
Exp memory simpleInterestFactor = mul_(
Exp({mantissa: borrowRateMantissa}),
blockDelta
);
uint interestAccumulated = mul_ScalarTruncate(
simpleInterestFactor,
borrowsPrior
);
uint totalBorrowsNew = interestAccumulated + borrowsPrior;
uint totalReservesNew = mul_ScalarTruncateAddUInt(
Exp({mantissa: reserveFactorMantissa}),
interestAccumulated,
reservesPrior
);
uint borrowIndexNew = mul_ScalarTruncateAddUInt(
simpleInterestFactor,
borrowIndexPrior,
borrowIndexPrior
);
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We write the previously calculated values into storage */
accrualBlockNumber = currentBlockNumber;
accrualBlockTimestamp = block.timestamp;
borrowIndex = borrowIndexNew;
totalBorrows = totalBorrowsNew;
totalReserves = totalReservesNew;
if (fullUtilizationRate != newfullUtilizationRate) {
emit UpdateRate(fullUtilizationRate, newfullUtilizationRate);
fullUtilizationRate = newfullUtilizationRate;
}
/* We emit an AccrueInterest event */
emit AccrueInterest(
cashPrior,
interestAccumulated,
borrowIndexNew,
totalBorrowsNew
);
return NO_ERROR;
}
/**
* @notice Sender supplies assets into the market and receives cTokens in exchange
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param mintAmount The amount of the underlying asset to supply
*/
function mintInternal(uint mintAmount) internal nonReentrant {
accrueInterest();
// mintFresh emits the actual Mint event if successful and logs on errors, so we don't need to
mintFresh(msg.sender, mintAmount);
}
/**
* @notice User supplies assets into the market and receives cTokens in exchange
* @dev Assumes interest has already been accrued up to the current block
* @param minter The address of the account which is supplying the assets
* @param mintAmount The amount of the underlying asset to supply
*/
function mintFresh(address minter, uint mintAmount) internal {
/* Fail if mint not allowed */
uint allowed = comptroller.mintAllowed(
address(this),
minter,
mintAmount
);
if (allowed != 0) {
revert MintComptrollerRejection(allowed);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
revert MintFreshnessCheck();
}
Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal()});
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We call `doTransferIn` for the minter and the mintAmount.
* Note: The cToken must handle variations between ERC-20 and ETH underlying.
* `doTransferIn` reverts if anything goes wrong, since we can't be sure if
* side-effects occurred. The function returns the amount actually transferred,
* in case of a fee. On success, the cToken holds an additional `actualMintAmount`
* of cash.
*/
uint actualMintAmount = doTransferIn(minter, mintAmount);
/*
* We get the current exchange rate and calculate the number of cTokens to be minted:
* mintTokens = actualMintAmount / exchangeRate
*/
uint mintTokens = div_(actualMintAmount, exchangeRate);
// sherlock issue-253
// first depositor bug
if (totalSupply == 0)
{
totalSupply = 1000;
accountTokens[address(0)] = 1000;
mintTokens -= 1000;
}
/*
* We calculate the new total supply of cTokens and minter token balance, checking for overflow:
* totalSupplyNew = totalSupply + mintTokens
* accountTokensNew = accountTokens[minter] + mintTokens
* And write them into storage
*/
totalSupply = totalSupply + mintTokens;
accountTokens[minter] = accountTokens[minter] + mintTokens;
/* We emit a Mint event, and a Transfer event */
emit Mint(minter, actualMintAmount, mintTokens);
emit Transfer(address(this), minter, mintTokens);
/* We call the defense hook */
// unused function
// comptroller.mintVerify(address(this), minter, actualMintAmount, mintTokens);
}
/**
* @notice Sender redeems cTokens in exchange for the underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param redeemTokens The number of cTokens to redeem into underlying
*/
function redeemInternal(uint redeemTokens) internal nonReentrant {
accrueInterest();
// redeemFresh emits redeem-specific logs on errors, so we don't need to
redeemFresh(payable(msg.sender), redeemTokens, 0);
}
/**
* @notice Sender redeems cTokens in exchange for a specified amount of underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param redeemAmount The amount of underlying to receive from redeeming cTokens
*/
function redeemUnderlyingInternal(uint redeemAmount) internal nonReentrant {
accrueInterest();
// redeemFresh emits redeem-specific logs on errors, so we don't need to
redeemFresh(payable(msg.sender), 0, redeemAmount);
}
/**
* @notice User redeems cTokens in exchange for the underlying asset
* @dev Assumes interest has already been accrued up to the current block
* @param redeemer The address of the account which is redeeming the tokens
* @param redeemTokensIn The number of cTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero)
* @param redeemAmountIn The number of underlying tokens to receive from redeeming cTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero)
*/
function redeemFresh(
address payable redeemer,
uint redeemTokensIn,
uint redeemAmountIn
) internal virtual {
require(
redeemTokensIn == 0 || redeemAmountIn == 0,
"one of redeemTokensIn or redeemAmountIn must be zero"
);
/* exchangeRate = invoke Exchange Rate Stored() */
Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal()});
uint redeemTokens;
uint redeemAmount;
/* If redeemTokensIn > 0: */
if (redeemTokensIn > 0) {
/*
* We calculate the exchange rate and the amount of underlying to be redeemed:
* redeemTokens = redeemTokensIn
* redeemAmount = redeemTokensIn x exchangeRateCurrent
*/
redeemTokens = redeemTokensIn;
redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokensIn);
} else {
/*
* We get the current exchange rate and calculate the amount to be redeemed:
* redeemTokens = redeemAmountIn / exchangeRate
* redeemAmount = redeemAmountIn
*/
redeemTokens = div_(redeemAmountIn, exchangeRate);
redeemAmount = redeemAmountIn;
}
/* Fail if redeem not allowed */
uint allowed = comptroller.redeemAllowed(
address(this),
redeemer,
redeemTokens
);
if (allowed != 0) {
revert RedeemComptrollerRejection(allowed);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
revert RedeemFreshnessCheck();
}
/* Fail gracefully if protocol has insufficient cash */
if (getCashPrior() < redeemAmount) {
revert RedeemTransferOutNotPossible();
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We write the previously calculated values into storage.
* Note: Avoid token reentrancy attacks by writing reduced supply before external transfer.
*/
totalSupply = totalSupply - redeemTokens;
accountTokens[redeemer] = accountTokens[redeemer] - redeemTokens;
/*
* We invoke doTransferOut for the redeemer and the redeemAmount.
* Note: The cToken must handle variations between ERC-20 and ETH underlying.
* On success, the cToken has redeemAmount less of cash.
* doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
*/
doTransferOut(redeemer, redeemAmount);
/* We emit a Transfer event, and a Redeem event */
emit Transfer(redeemer, address(this), redeemTokens);
emit Redeem(redeemer, redeemAmount, redeemTokens);
/* We call the defense hook */
comptroller.redeemVerify(
address(this),
redeemer,
redeemAmount,
redeemTokens
);
}
/**
* @notice Sender borrows assets from the protocol to their own address
* @param borrowAmount The amount of the underlying asset to borrow
*/
function borrowInternal(uint borrowAmount) internal nonReentrant {
accrueInterest();
// borrowFresh emits borrow-specific logs on errors, so we don't need to
borrowFresh(payable(msg.sender), borrowAmount);
}
/**
* @notice Users borrow assets from the protocol to their own address
* @param borrowAmount The amount of the underlying asset to borrow
*/
function borrowFresh(
address payable borrower,
uint borrowAmount
) internal virtual {
borrowFreshNoTransfer(borrower, borrowAmount);
/*
* We invoke doTransferOut for the borrower and the borrowAmount.
* Note: The cToken must handle variations between ERC-20 and ETH underlying.
* On success, the cToken borrowAmount less of cash.
* doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
*/
doTransferOut(borrower, borrowAmount);
}
// function borrowFreshOnBehalf(address payable borrower,address payable receiver, uint borrowAmount) virtual internal
// {
// borrowFreshNoTransfer(borrower, borrowAmount);
// /*
// * We invoke doTransferOut for the receiver and the borrowAmount.
// * Note: The cToken must handle variations between ERC-20 and ETH underlying.
// * On success, the cToken borrowAmount less of cash.
// * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
// */
// doTransferOut(receiver, borrowAmount);
// }
/**
* @notice Users borrow assets from the protocol to their own address
* @param borrowAmount The amount of the underlying asset to borrow
*/
function borrowFreshNoTransfer(
address payable borrower,
uint borrowAmount
) internal virtual {
/* Fail if borrow not allowed */
uint allowed = comptroller.borrowAllowed(
address(this),
borrower,
borrowAmount
);
if (allowed != 0) {
revert BorrowComptrollerRejection(allowed);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
revert BorrowFreshnessCheck();
}
/* Fail gracefully if protocol has insufficient underlying cash */
if (getCashPrior() < borrowAmount) {
revert BorrowCashNotAvailable();
}
/*
* We calculate the new borrower and total borrow balances, failing on overflow:
* accountBorrowNew = accountBorrow + borrowAmount
* totalBorrowsNew = totalBorrows + borrowAmount
*/
uint accountBorrowsPrev = borrowBalanceStoredInternal(borrower);
uint accountBorrowsNew = accountBorrowsPrev + borrowAmount;
uint totalBorrowsNew = totalBorrows + borrowAmount;
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We write the previously calculated values into storage.
* Note: Avoid token reentrancy attacks by writing increased borrow before external transfer.
`*/
accountBorrows[borrower].principal = accountBorrowsNew;
accountBorrows[borrower].interestIndex = borrowIndex;
totalBorrows = totalBorrowsNew;
/* We emit a Borrow event */
emit Borrow(borrower, borrowAmount, accountBorrowsNew, totalBorrowsNew);
}
/**
* @notice Sender repays their own borrow
* @param repayAmount The amount to repay, or -1 for the full outstanding amount
*/
function repayBorrowInternal(uint repayAmount) internal nonReentrant {
accrueInterest();
// repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to
repayBorrowFresh(msg.sender, msg.sender, repayAmount);
}
/**
* @notice Sender repays a borrow belonging to borrower
* @param borrower the account with the debt being payed off
* @param repayAmount The amount to repay, or -1 for the full outstanding amount
*/
function repayBorrowBehalfInternal(
address borrower,
uint repayAmount
) internal nonReentrant {
accrueInterest();
// repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to
repayBorrowFresh(msg.sender, borrower, repayAmount);
}
/**
* @notice Borrows are repaid by another user (possibly the borrower).
* @param payer the account paying off the borrow
* @param borrower the account with the debt being payed off
* @param repayAmount the amount of underlying tokens being returned, or -1 for the full outstanding amount
* @return (uint) the actual repayment amount.
*/
function repayBorrowFresh(
address payer,
address borrower,
uint repayAmount
) internal virtual returns (uint) {
/* Fail if repayBorrow not allowed */
uint allowed = comptroller.repayBorrowAllowed(
address(this),
payer,
borrower,
repayAmount
);
if (allowed != 0) {
revert RepayBorrowComptrollerRejection(allowed);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
revert RepayBorrowFreshnessCheck();
}
/* We fetch the amount the borrower owes, with accumulated interest */
uint accountBorrowsPrev = borrowBalanceStoredInternal(borrower);
/* If repayAmount == -1, repayAmount = accountBorrows */
uint repayAmountFinal = repayAmount == type(uint).max
? accountBorrowsPrev
: repayAmount;
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We call doTransferIn for the payer and the repayAmount
* Note: The cToken must handle variations between ERC-20 and ETH underlying.
* On success, the cToken holds an additional repayAmount of cash.
* doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.
* it returns the amount actually transferred, in case of a fee.
*/
// Numa modif, we can use this function to repay leverage and payer is CToken (amount comes from flashloan)
// so we need to handle that case
uint actualRepayAmount = repayAmount;
if (payer != address(this)) {
// transfer needed
actualRepayAmount = doTransferIn(payer, repayAmountFinal);
}
/*
* We calculate the new borrower and total borrow balances, failing on underflow:
* accountBorrowsNew = accountBorrows - actualRepayAmount
* totalBorrowsNew = totalBorrows - actualRepayAmount
*/
uint accountBorrowsNew = accountBorrowsPrev - actualRepayAmount;
uint totalBorrowsNew = totalBorrows - actualRepayAmount;
/* We write the previously calculated values into storage */
accountBorrows[borrower].principal = accountBorrowsNew;
accountBorrows[borrower].interestIndex = borrowIndex;
totalBorrows = totalBorrowsNew;
/* We emit a RepayBorrow event */
emit RepayBorrow(
payer,
borrower,
actualRepayAmount,
accountBorrowsNew,
totalBorrowsNew
);
return actualRepayAmount;
}
/**
* @notice The sender liquidates the borrowers collateral.
* The collateral seized is transferred to the liquidator.
* @param borrower The borrower of this cToken to be liquidated
* @param cTokenCollateral The market in which to seize collateral from the borrower
* @param repayAmount The amount of the underlying borrowed asset to repay
* @return badDebt the amount of baddebt the position had
*/
function liquidateBorrowInternal(
address borrower,
uint repayAmount,
CTokenInterface cTokenCollateral
) internal nonReentrant returns (uint){
accrueInterest();
uint error = cTokenCollateral.accrueInterest();
if (error != NO_ERROR) {
// accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed
revert LiquidateAccrueCollateralInterestFailed(error);
}
// liquidateBorrowFresh emits borrow-specific logs on errors, so we don't need to
return liquidateBorrowFresh(
msg.sender,
borrower,
repayAmount,
cTokenCollateral
);
}
function liquidateBadDebtInternal(
address borrower,
uint repayAmount,
uint percentageToTake,
CTokenInterface cTokenCollateral
) internal nonReentrant {
accrueInterest();
uint error = cTokenCollateral.accrueInterest();
if (error != NO_ERROR) {
// accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed
revert LiquidateAccrueCollateralInterestFailed(error);
}
liquidateBadDebtFresh(
msg.sender,
borrower,
repayAmount,
percentageToTake,
cTokenCollateral
);
}
/**
* @notice The liquidator liquidates the borrowers collateral.
* The collateral seized is transferred to the liquidator.
* @param borrower The borrower of this cToken to be liquidated
* @param liquidator The address repaying the borrow and seizing collateral
* @param cTokenCollateral The market in which to seize collateral from the borrower
* @param repayAmount The amount of the underlying borrowed asset to repay
* @return badDebt the amount of baddebt the position had
*/
function liquidateBorrowFresh(
address liquidator,
address borrower,
uint repayAmount,
CTokenInterface cTokenCollateral
) internal returns (uint) {
/* Fail if liquidate not allowed */
(uint allowed,uint badDebt,uint restOfDebt) = comptroller.liquidateBorrowAllowed(
address(this),
address(cTokenCollateral),
borrower,
repayAmount
);
if (allowed != 0) {
revert LiquidateComptrollerRejection(allowed);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
revert LiquidateFreshnessCheck();
}
/* Verify cTokenCollateral market's block number equals current block number */
if (cTokenCollateral.accrualBlockNumber() != getBlockNumber()) {
revert LiquidateCollateralFreshnessCheck();
}
/* Fail if borrower = liquidator */
if (borrower == liquidator) {
revert LiquidateLiquidatorIsBorrower();
}
/* Fail if repayAmount = 0 */
if (repayAmount == 0) {
revert LiquidateCloseAmountIsZero();
}
/* Fail if repayAmount = -1 */
if (repayAmount == type(uint).max) {
revert LiquidateCloseAmountIsUintMax();
}
/* Fail if repayBorrow fails */
uint actualRepayAmount = repayBorrowFresh(
liquidator,
borrower,
repayAmount
);
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We calculate the number of collateral tokens that will be seized */
(uint amountSeizeError, uint seizeTokens) = comptroller
.liquidateCalculateSeizeTokens(
address(this),
address(cTokenCollateral),
actualRepayAmount
);
require(
amountSeizeError == NO_ERROR,
"LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED"
);
if (cTokenCollateral.balanceOf(borrower) < seizeTokens) {
// sherlock 101 153
// not enough collateral to pay liquidator with incentives
// if we are not in bad debt territory, it will still be profitable to take all collateral
// but we do this only if it's a full liquidation, so that we don't become in bad debt after
if ((badDebt == 0) && (restOfDebt == 0)) {
// no bad debt
// full borrow balance liquidation
seizeTokens = cTokenCollateral.balanceOf(borrower);
}
else
{
revert("LIQUIDATE_SEIZE_TOO_MUCH") ;
}
}
// If this is also the collateral, run seizeInternal to avoid re-entrancy, otherwise make an external call
if (address(cTokenCollateral) == address(this)) {
seizeInternal(address(this), liquidator, borrower, seizeTokens);
} else {
require(
cTokenCollateral.seize(liquidator, borrower, seizeTokens) ==
NO_ERROR,
"token seizure failed"
);
}
/* We emit a LiquidateBorrow event */
emit LiquidateBorrow(
liquidator,
borrower,
actualRepayAmount,
address(cTokenCollateral),
seizeTokens
);
// sherlock 101 153 returning bad debt so that vault can decide whether to clip
// profit or not
return badDebt;
}
function liquidateBadDebtFresh(
address liquidator,
address borrower,
uint repayAmount,
uint percentageToTake,
CTokenInterface cTokenCollateral
) internal {
/* Fail if liquidate not allowed */
uint allowed = comptroller.liquidateBadDebtAllowed(
address(this),
address(cTokenCollateral),
liquidator,
borrower,
repayAmount
);
if (allowed != 0) {
revert LiquidateComptrollerRejection(allowed);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
revert LiquidateFreshnessCheck();
}
/* Verify cTokenCollateral market's block number equals current block number */
if (cTokenCollateral.accrualBlockNumber() != getBlockNumber()) {
revert LiquidateCollateralFreshnessCheck();
}
/* Fail if borrower = liquidator */
if (borrower == liquidator) {
revert LiquidateLiquidatorIsBorrower();
}
/* Fail if repayAmount = 0 */
if (repayAmount == 0) {
revert LiquidateCloseAmountIsZero();
}
// /* Fail if repayAmount = -1 */
// if (repayAmount == type(uint).max) {
// revert LiquidateCloseAmountIsUintMax();
// }
// This is moved after evaluating seize tokens as we need to use borrow balance
/* Fail if repayBorrow fails */
uint actualRepayAmount;
if (repayAmount > 0)
actualRepayAmount = repayBorrowFresh(
liquidator,
borrower,
repayAmount
);
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We calculate the number of collateral tokens that will be seized */
(uint amountSeizeError, uint seizeTokens) = comptroller
.liquidateBadDebtCalculateSeizeTokensAfterRepay(
address(cTokenCollateral),
borrower,
percentageToTake
);
require(
amountSeizeError == NO_ERROR,
"LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED"
);
/* Revert if borrower collateral token balance < seizeTokens */
require(
cTokenCollateral.balanceOf(borrower) >= seizeTokens,
"LIQUIDATE_SEIZE_TOO_MUCH"
);
//If this is also the collateral, run seizeInternal to avoid re-entrancy, otherwise make an external call
if (address(cTokenCollateral) == address(this)) {
seizeBadDebtInternal(
address(this),
liquidator,
borrower,
seizeTokens
);
} else {
require(
cTokenCollateral.seizeBadDebt(
liquidator,
borrower,
seizeTokens
) == NO_ERROR,
"token seizure failed"
);
}
/* We emit a LiquidateBorrow event */
emit LiquidateBadDebt(
liquidator,
borrower,
actualRepayAmount,
address(cTokenCollateral),
seizeTokens
);
}
/**
* @notice Transfers collateral tokens (this market) to the liquidator.
* @dev Will fail unless called by another cToken during the process of liquidation.
* Its absolutely critical to use msg.sender as the borrowed cToken and not a parameter.
* @param liquidator The account receiving seized collateral
* @param borrower The account having collateral seized
* @param seizeTokens The number of cTokens to seize
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function seize(
address liquidator,
address borrower,
uint seizeTokens
) external override nonReentrant returns (uint) {
seizeInternal(msg.sender, liquidator, borrower, seizeTokens);
return NO_ERROR;
}
function seizeBadDebt(
address liquidator,
address borrower,
uint seizeTokens
) external override nonReentrant returns (uint) {
seizeBadDebtInternal(msg.sender, liquidator, borrower, seizeTokens);
return NO_ERROR;
}
/**
* @notice Transfers collateral tokens (this market) to the liquidator.
* @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another CToken.
* Its absolutely critical to use msg.sender as the seizer cToken and not a parameter.
* @param seizerToken The contract seizing the collateral (i.e. borrowed cToken)
* @param liquidator The account receiving seized collateral
* @param borrower The account having collateral seized
* @param seizeTokens The number of cTokens to seize
*/
function seizeInternal(
address seizerToken,
address liquidator,
address borrower,
uint seizeTokens
) internal {
/* Fail if seize not allowed */
uint allowed = comptroller.seizeAllowed(
address(this),
seizerToken,
liquidator,
borrower,
seizeTokens
);
if (allowed != 0) {
revert LiquidateSeizeComptrollerRejection(allowed);
}
/* Fail if borrower = liquidator */
if (borrower == liquidator) {
revert LiquidateSeizeLiquidatorIsBorrower();
}
/*
* We calculate the new borrower and liquidator token balances, failing on underflow/overflow:
* borrowerTokensNew = accountTokens[borrower] - seizeTokens
* liquidatorTokensNew = accountTokens[liquidator] + seizeTokens
*/
uint protocolSeizeTokens = mul_(
seizeTokens,
Exp({mantissa: protocolSeizeShareMantissa})
);
uint liquidatorSeizeTokens = seizeTokens - protocolSeizeTokens;
Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal()});
uint protocolSeizeAmount = mul_ScalarTruncate(
exchangeRate,
protocolSeizeTokens
);
uint totalReservesNew = totalReserves + protocolSeizeAmount;
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We write the calculated values into storage */
totalReserves = totalReservesNew;
totalSupply = totalSupply - protocolSeizeTokens;
accountTokens[borrower] = accountTokens[borrower] - seizeTokens;
accountTokens[liquidator] =
accountTokens[liquidator] +
liquidatorSeizeTokens;
/* Emit a Transfer event */
emit Transfer(borrower, liquidator, liquidatorSeizeTokens);
emit Transfer(borrower, address(this), protocolSeizeTokens);
emit ReservesAdded(
address(this),
protocolSeizeAmount,
totalReservesNew
);
}
function seizeBadDebtInternal(
address seizerToken,
address liquidator,
address borrower,
uint seizeTokens
) internal {
/* Fail if seize not allowed */
uint allowed = comptroller.seizeAllowed(
address(this),
seizerToken,
liquidator,
borrower,
seizeTokens
);
if (allowed != 0) {
revert LiquidateSeizeComptrollerRejection(allowed);
}
/* Fail if borrower = liquidator */
if (borrower == liquidator) {
revert LiquidateSeizeLiquidatorIsBorrower();
}
/*
* We calculate the new borrower and liquidator token balances, failing on underflow/overflow:
* borrowerTokensNew = accountTokens[borrower] - seizeTokens
* liquidatorTokensNew = accountTokens[liquidator] + seizeTokens
*/
// everything goes to liquidator
uint liquidatorSeizeTokens = seizeTokens;
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We write the calculated values into storage */
accountTokens[borrower] = accountTokens[borrower] - seizeTokens;
accountTokens[liquidator] =
accountTokens[liquidator] +
liquidatorSeizeTokens;
/* Emit a Transfer event */
emit Transfer(borrower, liquidator, liquidatorSeizeTokens);
}
/*** Admin Functions ***/
/**
* @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
* @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
* @param newPendingAdmin New pending admin.
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setPendingAdmin(
address payable newPendingAdmin
) external override returns (uint) {
// Check caller = admin
if (msg.sender != admin) {
revert SetPendingAdminOwnerCheck();
}
// Save current value, if any, for inclusion in log
address oldPendingAdmin = pendingAdmin;
// Store pendingAdmin with value newPendingAdmin
pendingAdmin = newPendingAdmin;
// Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin)
emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);
return NO_ERROR;
}
/**
* @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin
* @dev Admin function for pending admin to accept role and update admin
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _acceptAdmin() external override returns (uint) {
// Check caller is pendingAdmin and pendingAdmin ≠ address(0)
if (msg.sender != pendingAdmin || msg.sender == address(0)) {
revert AcceptAdminPendingAdminCheck();
}
// Save current values for inclusion in log
address oldAdmin = admin;
address oldPendingAdmin = pendingAdmin;
// Store admin with value pendingAdmin
admin = pendingAdmin;
// Clear the pending value
pendingAdmin = payable(address(0));
emit NewAdmin(oldAdmin, admin);
emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);
return NO_ERROR;
}
/**
* @notice Sets a new comptroller for the market
* @dev Admin function to set a new comptroller
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setComptroller(
ComptrollerInterface newComptroller
) public override returns (uint) {
// Check caller is admin
if (msg.sender != admin) {
revert SetComptrollerOwnerCheck();
}
ComptrollerInterface oldComptroller = comptroller;
// Ensure invoke comptroller.isComptroller() returns true
require(newComptroller.isComptroller(), "marker method returned false");
// Set market's comptroller to newComptroller
comptroller = newComptroller;
// Emit NewComptroller(oldComptroller, newComptroller)
emit NewComptroller(oldComptroller, newComptroller);
return NO_ERROR;
}
/**
* @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh
* @dev Admin function to accrue interest and set a new reserve factor
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setReserveFactor(
uint newReserveFactorMantissa
) external override nonReentrant returns (uint) {
accrueInterest();
// _setReserveFactorFresh emits reserve-factor-specific logs on errors, so we don't need to.
return _setReserveFactorFresh(newReserveFactorMantissa);
}
/**
* @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual)
* @dev Admin function to set a new reserve factor
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setReserveFactorFresh(
uint newReserveFactorMantissa
) internal returns (uint) {
// Check caller is admin
if (msg.sender != admin) {
revert SetReserveFactorAdminCheck();
}
// Verify market's block number equals current block number
if (accrualBlockNumber != getBlockNumber()) {
revert SetReserveFactorFreshCheck();
}
// Check newReserveFactor ≤ maxReserveFactor
if (newReserveFactorMantissa > reserveFactorMaxMantissa) {
revert SetReserveFactorBoundsCheck();
}
uint oldReserveFactorMantissa = reserveFactorMantissa;
reserveFactorMantissa = newReserveFactorMantissa;
emit NewReserveFactor(
oldReserveFactorMantissa,
newReserveFactorMantissa
);
return NO_ERROR;
}
/**
* @notice Accrues interest and reduces reserves by transferring from msg.sender
* @param addAmount Amount of addition to reserves
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _addReservesInternal(
uint addAmount
) internal nonReentrant returns (uint) {
accrueInterest();
// _addReservesFresh emits reserve-addition-specific logs on errors, so we don't need to.
_addReservesFresh(addAmount);
return NO_ERROR;
}
/**
* @notice Add reserves by transferring from caller
* @dev Requires fresh interest accrual
* @param addAmount Amount of addition to reserves
* @return (uint, uint) An error code (0=success, otherwise a failure (see ErrorReporter.sol for details)) and the actual amount added, net token fees
*/
function _addReservesFresh(uint addAmount) internal returns (uint, uint) {
// totalReserves + actualAddAmount
uint totalReservesNew;
uint actualAddAmount;
// We fail gracefully unless market's block number equals current block number
if (accrualBlockNumber != getBlockNumber()) {
revert AddReservesFactorFreshCheck(actualAddAmount);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We call doTransferIn for the caller and the addAmount
* Note: The cToken must handle variations between ERC-20 and ETH underlying.
* On success, the cToken holds an additional addAmount of cash.
* doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.
* it returns the amount actually transferred, in case of a fee.
*/
actualAddAmount = doTransferIn(msg.sender, addAmount);
totalReservesNew = totalReserves + actualAddAmount;
// Store reserves[n+1] = reserves[n] + actualAddAmount
totalReserves = totalReservesNew;
/* Emit NewReserves(admin, actualAddAmount, reserves[n+1]) */
emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew);
/* Return (NO_ERROR, actualAddAmount) */
return (NO_ERROR, actualAddAmount);
}
/**
* @notice Accrues interest and reduces reserves by transferring to admin
* @param reduceAmount Amount of reduction to reserves
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _reduceReserves(
uint reduceAmount
) external override nonReentrant returns (uint) {
accrueInterest();
// _reduceReservesFresh emits reserve-reduction-specific logs on errors, so we don't need to.
return _reduceReservesFresh(reduceAmount);
}
/**
* @notice Reduces reserves by transferring to admin
* @dev Requires fresh interest accrual
* @param reduceAmount Amount of reduction to reserves
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _reduceReservesFresh(uint reduceAmount) internal returns (uint) {
// totalReserves - reduceAmount
uint totalReservesNew;
// Check caller is admin
if (msg.sender != admin) {
revert ReduceReservesAdminCheck();
}
// We fail gracefully unless market's block number equals current block number
if (accrualBlockNumber != getBlockNumber()) {
revert ReduceReservesFreshCheck();
}
// Fail gracefully if protocol has insufficient underlying cash
if (getCashPrior() < reduceAmount) {
revert ReduceReservesCashNotAvailable();
}
// Check reduceAmount ≤ reserves[n] (totalReserves)
if (reduceAmount > totalReserves) {
revert ReduceReservesCashValidation();
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
totalReservesNew = totalReserves - reduceAmount;
// Store reserves[n+1] = reserves[n] - reduceAmount
totalReserves = totalReservesNew;
// doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
doTransferOut(admin, reduceAmount);
emit ReservesReduced(admin, reduceAmount, totalReservesNew);
return NO_ERROR;
}
/**
* @notice change borrowRateMaxMantissa
* @dev needed because values should be adjustedbased on chain block time
* @param _borrowRateMaxMantissa the new value
*/
function _setBorrowRateMaxMantissa(
uint _borrowRateMaxMantissa
) public override
{
if (msg.sender != admin) {
revert SetBorrowRateMaxMantissaOwnerCheck();
}
borrowRateMaxMantissa = _borrowRateMaxMantissa;
}
/**
* @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh
* @dev Admin function to accrue interest and update the interest rate model
* @param newInterestRateModel the new interest rate model to use
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setInterestRateModel(
InterestRateModel newInterestRateModel
) public override returns (uint) {
accrueInterest();
// _setInterestRateModelFresh emits interest-rate-model-update-specific logs on errors, so we don't need to.
return _setInterestRateModelFresh(newInterestRateModel);
}
/**
* @notice updates the interest rate model (*requires fresh interest accrual)
* @dev Admin function to update the interest rate model
* @param newInterestRateModel the new interest rate model to use
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function _setInterestRateModelFresh(
InterestRateModel newInterestRateModel
) internal returns (uint) {
// Used to store old model for use in the event that is emitted on success
InterestRateModel oldInterestRateModel;
// Check caller is admin
if (msg.sender != admin) {
revert SetInterestRateModelOwnerCheck();
}
// We fail gracefully unless market's block number equals current block number
if (accrualBlockNumber != getBlockNumber()) {
revert SetInterestRateModelFreshCheck();
}
// Track the market's current interest rate model
oldInterestRateModel = interestRateModel;
// Ensure invoke newInterestRateModel.isInterestRateModel() returns true
require(
newInterestRateModel.isInterestRateModel(),
"marker method returned false"
);
// Set the interest rate model to newInterestRateModel
interestRateModel = newInterestRateModel;
// Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel)
emit NewMarketInterestRateModel(
oldInterestRateModel,
newInterestRateModel
);
return NO_ERROR;
}
/*** Safe Token ***/
/**
* @notice Gets balance of this contract in terms of the underlying
* @dev This excludes the value of the current message, if any
* @return The quantity of underlying owned by this contract
*/
function getCashPrior() internal view virtual returns (uint);
/**
* @dev Performs a transfer in, reverting upon failure. Returns the amount actually transferred to the protocol, in case of a fee.
* This may revert due to insufficient balance or insufficient allowance.
*/
function doTransferIn(
address from,
uint amount
) internal virtual returns (uint);
/**
* @dev Performs a transfer out, ideally returning an explanatory error code upon failure rather than reverting.
* If caller has not called checked protocol's balance, may revert due to insufficient cash held in the contract.
* If caller has checked protocol's balance, and verified it is >= amount, this should not revert in normal conditions.
*/
function doTransferOut(address payable to, uint amount) internal virtual;
/*** Reentrancy Guard ***/
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
*/
modifier nonReentrant() {
require(_notEntered, "re-entered");
_notEntered = false;
_;
_notEntered = true; // get a gas-refund post-Istanbul
}
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.20;
abstract contract ComptrollerInterface {
/// @notice Indicator that this is a Comptroller contract (for inspection)
bool public constant isComptroller = true;
/*** Assets You Are In ***/
function enterMarkets(
address[] calldata cTokens
) external virtual returns (uint[] memory);
function exitMarket(address cToken) external virtual returns (uint);
/*** Policy Hooks ***/
function mintAllowed(
address cToken,
address minter,
uint mintAmount
) external virtual returns (uint);
// function mintVerify(
// address cToken,
// address minter,
// uint mintAmount,
// uint mintTokens
// ) external virtual;
function redeemAllowed(
address cToken,
address redeemer,
uint redeemTokens
) external virtual returns (uint);
function redeemVerify(
address cToken,
address redeemer,
uint redeemAmount,
uint redeemTokens
) external virtual;
function borrowAllowed(
address cToken,
address borrower,
uint borrowAmount
) external virtual returns (uint);
function borrowVerify(
address cToken,
address borrower,
uint borrowAmount
) external virtual;
function repayBorrowAllowed(
address cToken,
address payer,
address borrower,
uint repayAmount
) external virtual returns (uint);
function repayBorrowVerify(
address cToken,
address payer,
address borrower,
uint repayAmount,
uint borrowerIndex
) external virtual;
function liquidateBadDebtAllowed(
address cTokenBorrowed,
address cTokenCollateral,
address liquidator,
address borrower,
uint repayAmount
) external virtual returns (uint);
function liquidateBorrowAllowed(
address cTokenBorrowed,
address cTokenCollateral,
address borrower,
uint repayAmount
) external virtual returns (uint,uint,uint);
function liquidateBorrowVerify(
address cTokenBorrowed,
address cTokenCollateral,
address liquidator,
address borrower,
uint repayAmount,
uint seizeTokens
) external virtual;
function seizeAllowed(
address cTokenCollateral,
address cTokenBorrowed,
address liquidator,
address borrower,
uint seizeTokens
) external virtual returns (uint);
function seizeVerify(
address cTokenCollateral,
address cTokenBorrowed,
address liquidator,
address borrower,
uint seizeTokens
) external virtual;
function transferAllowed(
address cToken,
address src,
address dst,
uint transferTokens
) external virtual returns (uint);
function transferVerify(
address cToken,
address src,
address dst,
uint transferTokens
) external virtual;
/*** Liquidity/Liquidation Calculations ***/
function liquidateCalculateSeizeTokens(
address cTokenBorrowed,
address cTokenCollateral,
uint repayAmount
) external view virtual returns (uint, uint);
// function liquidateBadDebtCalculateSeizeTokens(
// address cTokenBorrowed,
// address cTokenCollateral,
// address borrower,
// uint actualRepayAmount
// ) external view virtual returns (uint, uint);
function liquidateBadDebtCalculateSeizeTokensAfterRepay(
address cTokenCollateral,
address borrower,
uint percentageToTake
) external view virtual returns (uint, uint);
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.20;
import "./ComptrollerInterface.sol";
import "./InterestRateModel.sol";
import "./EIP20NonStandardInterface.sol";
import "./ErrorReporter.sol";
contract CTokenStorage {
/**
* @dev Guard variable for re-entrancy checks
*/
bool internal _notEntered;
/**
* @notice EIP-20 token name for this token
*/
string public name;
/**
* @notice EIP-20 token symbol for this token
*/
string public symbol;
/**
* @notice EIP-20 token decimals for this token
*/
uint8 public decimals;
// Maximum borrow rate that can ever be applied (.0005% / block)
uint public borrowRateMaxMantissa = 0.0005e16;
// Maximum fraction of interest that can be set aside for reserves
uint internal constant reserveFactorMaxMantissa = 1e18;
/**
* @notice Administrator for this contract
*/
address payable public admin;
/**
* @notice Pending administrator for this contract
*/
address payable public pendingAdmin;
/**
* @notice Contract which oversees inter-cToken operations
*/
ComptrollerInterface public comptroller;
/**
* @notice Model which tells what the current interest rate should be
*/
InterestRateModel public interestRateModel;
// Initial exchange rate used when minting the first CTokens (used when totalSupply = 0)
uint internal initialExchangeRateMantissa;
/**
* @notice Fraction of interest currently set aside for reserves
*/
uint public reserveFactorMantissa;
/**
* @notice Block number that interest was last accrued at
*/
uint public accrualBlockNumber;
/**
* @notice Block timestamp that interest was last accrued at
*/
uint public accrualBlockTimestamp;
uint public fullUtilizationRate;
/**
* @notice Accumulator of the total earned interest rate since the opening of the market
*/
uint public borrowIndex;
/**
* @notice Total amount of outstanding borrows of the underlying in this market
*/
uint public totalBorrows;
/**
* @notice Total amount of reserves of the underlying held in this market
*/
uint public totalReserves;
/**
* @notice Total number of tokens in circulation
*/
uint public totalSupply;
// Official record of token balances for each account
mapping(address => uint) internal accountTokens;
// Approved token transfer amounts on behalf of others
mapping(address => mapping(address => uint)) internal transferAllowances;
/**
* @notice Container for borrow balance information
* @member principal Total balance (with accrued interest), after applying the most recent balance-changing action
* @member interestIndex Global borrowIndex as of the most recent balance-changing action
*/
struct BorrowSnapshot {
uint principal;
uint interestIndex;
}
// Mapping of account addresses to outstanding borrow balances
mapping(address => BorrowSnapshot) internal accountBorrows;
/**
* @notice Share of seized collateral that is added to reserves
*/
//uint public constant protocolSeizeShareMantissa = 2.8e16; //2.8%
uint public constant protocolSeizeShareMantissa = 0;
}
abstract contract CTokenInterface is CTokenStorage {
/**
* @notice Indicator that this is a CToken contract (for inspection)
*/
bool public constant isCToken = true;
/*** Market Events ***/
/**
* @notice Event emitted when interest is accrued
*/
event AccrueInterest(
uint cashPrior,
uint interestAccumulated,
uint borrowIndex,
uint totalBorrows
);
/**
* @notice Event emitted when tokens are minted
*/
event Mint(address minter, uint mintAmount, uint mintTokens);
/**
* @notice Event emitted when tokens are redeemed
*/
event Redeem(address redeemer, uint redeemAmount, uint redeemTokens);
/**
* @notice Event emitted when underlying is borrowed
*/
event Borrow(
address borrower,
uint borrowAmount,
uint accountBorrows,
uint totalBorrows
);
/**
* @notice Event emitted when a borrow is repaid
*/
event RepayBorrow(
address payer,
address borrower,
uint repayAmount,
uint accountBorrows,
uint totalBorrows
);
/**
* @notice Event emitted when a borrow is liquidated
*/
event LiquidateBorrow(
address liquidator,
address borrower,
uint repayAmount,
address cTokenCollateral,
uint seizeTokens
);
event LiquidateBadDebt(
address liquidator,
address borrower,
uint repayAmount,
address cTokenCollateral,
uint seizeTokens
);
/// @notice The ```UpdateRate``` event is emitted when the interest rate is updated
/// @param oldFullUtilizationRate The old full utilization rate
/// @param newFullUtilizationRate The new full utilization rate
event UpdateRate(
uint256 oldFullUtilizationRate,
uint256 newFullUtilizationRate
);
/*** Admin Events ***/
/**
* @notice Event emitted when pendingAdmin is changed
*/
event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);
/**
* @notice Event emitted when pendingAdmin is accepted, which means admin is updated
*/
event NewAdmin(address oldAdmin, address newAdmin);
/**
* @notice Event emitted when comptroller is changed
*/
event NewComptroller(
ComptrollerInterface oldComptroller,
ComptrollerInterface newComptroller
);
/**
* @notice Event emitted when interestRateModel is changed
*/
event NewMarketInterestRateModel(
InterestRateModel oldInterestRateModel,
InterestRateModel newInterestRateModel
);
/**
* @notice Event emitted when the reserve factor is changed
*/
event NewReserveFactor(
uint oldReserveFactorMantissa,
uint newReserveFactorMantissa
);
/**
* @notice Event emitted when the reserves are added
*/
event ReservesAdded(
address benefactor,
uint addAmount,
uint newTotalReserves
);
/**
* @notice Event emitted when the reserves are reduced
*/
event ReservesReduced(
address admin,
uint reduceAmount,
uint newTotalReserves
);
/**
* @notice EIP20 Transfer event
*/
event Transfer(address indexed from, address indexed to, uint amount);
/**
* @notice EIP20 Approval event
*/
event Approval(address indexed owner, address indexed spender, uint amount);
/*** User Interface ***/
function transfer(address dst, uint amount) external virtual returns (bool);
function transferFrom(
address src,
address dst,
uint amount
) external virtual returns (bool);
function approve(
address spender,
uint amount
) external virtual returns (bool);
function allowance(
address owner,
address spender
) external view virtual returns (uint);
function balanceOf(address owner) external view virtual returns (uint);
function balanceOfUnderlying(address owner) external virtual returns (uint);
function getAccountSnapshot(
address account
) external view virtual returns (uint, uint, uint, uint);
function borrowRatePerBlock() external view virtual returns (uint);
function supplyRatePerBlock() external view virtual returns (uint);
function totalBorrowsCurrent() external virtual returns (uint);
function borrowBalanceCurrent(
address account
) external virtual returns (uint);
function borrowBalanceStored(
address account
) external view virtual returns (uint);
function exchangeRateCurrent() external virtual returns (uint);
function exchangeRateStored() external view virtual returns (uint);
function getCash() external view virtual returns (uint);
function accrueInterest() external virtual returns (uint);
function seize(
address liquidator,
address borrower,
uint seizeTokens
) external virtual returns (uint);
function seizeBadDebt(
address liquidator,
address borrower,
uint seizeTokens
) external virtual returns (uint);
/*** Admin Functions ***/
function _setPendingAdmin(
address payable newPendingAdmin
) external virtual returns (uint);
function _acceptAdmin() external virtual returns (uint);
function _setComptroller(
ComptrollerInterface newComptroller
) external virtual returns (uint);
function _setReserveFactor(
uint newReserveFactorMantissa
) external virtual returns (uint);
function _reduceReserves(uint reduceAmount) external virtual returns (uint);
function _setInterestRateModel(
InterestRateModel newInterestRateModel
) external virtual returns (uint);
function _setBorrowRateMaxMantissa(
uint _borrowRateMaxMantissa
) external virtual;
}
contract CErc20Storage {
/**
* @notice Underlying asset for this CToken
*/
address public underlying;
}
abstract contract CErc20Interface is CErc20Storage {
/*** User Interface ***/
function mint(uint mintAmount) external virtual returns (uint);
function redeem(uint redeemTokens) external virtual returns (uint);
function redeemUnderlying(
uint redeemAmount
) external virtual returns (uint);
function borrow(uint borrowAmount) external virtual returns (uint);
function repayBorrow(uint repayAmount) external virtual returns (uint);
function repayBorrowBehalf(
address borrower,
uint repayAmount
) external virtual returns (uint);
function liquidateBorrow(
address borrower,
uint repayAmount,
CTokenInterface cTokenCollateral
) external virtual returns (uint,uint);
function liquidateBadDebt(
address borrower,
uint repayAmount,
uint percentageToTake,
CTokenInterface cTokenCollateral
) external virtual returns (uint);
function sweepToken(EIP20NonStandardInterface token) external virtual;
/*** Admin Functions ***/
function _addReserves(uint addAmount) external virtual returns (uint);
}
contract CDelegationStorage {
/**
* @notice Implementation address for this contract
*/
address public implementation;
}
abstract contract CDelegatorInterface is CDelegationStorage {
/**
* @notice Emitted when implementation is changed
*/
event NewImplementation(
address oldImplementation,
address newImplementation
);
/**
* @notice Called by the admin to update the implementation of the delegator
* @param implementation_ The address of the new implementation for delegation
* @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation
* @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation
*/
function _setImplementation(
address implementation_,
bool allowResign,
bytes memory becomeImplementationData
) external virtual;
}
abstract contract CDelegateInterface is CDelegationStorage {
/**
* @notice Called by the delegator on a delegate to initialize it for duty
* @dev Should revert if any issues arise which make it unfit for delegation
* @param data The encoded bytes data for any initialization
*/
function _becomeImplementation(bytes memory data) external virtual;
/**
* @notice Called by the delegator on a delegate to forfeit its responsibility
*/
function _resignImplementation() external virtual;
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.20;
contract ComptrollerErrorReporter {
enum Error {
NO_ERROR,
UNAUTHORIZED,
COMPTROLLER_MISMATCH,
INSUFFICIENT_SHORTFALL,
INSUFFICIENT_BADDEBT,
INSUFFICIENT_LIQUIDITY,
INVALID_CLOSE_FACTOR,
INVALID_COLLATERAL_FACTOR,
INVALID_LIQUIDATION_INCENTIVE,
MARKET_NOT_ENTERED, // no longer possible
MARKET_NOT_LISTED,
MARKET_ALREADY_LISTED,
MATH_ERROR,
NONZERO_BORROW_BALANCE,
PRICE_ERROR,
REJECTION,
SNAPSHOT_ERROR,
TOO_MANY_ASSETS,
TOO_MUCH_REPAY,
BAD_DEBT
}
enum FailureInfo {
ACCEPT_ADMIN_PENDING_ADMIN_CHECK,
ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK,
EXIT_MARKET_BALANCE_OWED,
EXIT_MARKET_REJECTION,
SET_CLOSE_FACTOR_OWNER_CHECK,
SET_CLOSE_FACTOR_VALIDATION,
SET_COLLATERAL_FACTOR_OWNER_CHECK,
SET_COLLATERAL_FACTOR_NO_EXISTS,
SET_COLLATERAL_FACTOR_VALIDATION,
SET_COLLATERAL_FACTOR_WITHOUT_PRICE,
SET_IMPLEMENTATION_OWNER_CHECK,
SET_LIQUIDATION_INCENTIVE_OWNER_CHECK,
SET_LIQUIDATION_INCENTIVE_VALIDATION,
SET_MAX_ASSETS_OWNER_CHECK,
SET_PENDING_ADMIN_OWNER_CHECK,
SET_PENDING_IMPLEMENTATION_OWNER_CHECK,
SET_PRICE_ORACLE_OWNER_CHECK,
SUPPORT_MARKET_EXISTS,
SUPPORT_MARKET_OWNER_CHECK,
SET_PAUSE_GUARDIAN_OWNER_CHECK
}
/**
* @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary
* contract-specific code that enables us to report opaque error codes from upgradeable contracts.
**/
event Failure(uint error, uint info, uint detail);
/**
* @dev use this when reporting a known error from the money market or a non-upgradeable collaborator
*/
function fail(Error err, FailureInfo info) internal returns (uint) {
emit Failure(uint(err), uint(info), 0);
return uint(err);
}
/**
* @dev use this when reporting an opaque error from an upgradeable collaborator contract
*/
function failOpaque(
Error err,
FailureInfo info,
uint opaqueError
) internal returns (uint) {
emit Failure(uint(err), uint(info), opaqueError);
return uint(err);
}
}
contract TokenErrorReporter {
uint public constant NO_ERROR = 0; // support legacy return codes
error TransferComptrollerRejection(uint256 errorCode);
error TransferNotAllowed();
error TransferNotEnough();
error TransferTooMuch();
error MintComptrollerRejection(uint256 errorCode);
error MintFreshnessCheck();
error RedeemComptrollerRejection(uint256 errorCode);
error RedeemFreshnessCheck();
error RedeemTransferOutNotPossible();
error BorrowComptrollerRejection(uint256 errorCode);
error BorrowFreshnessCheck();
error BorrowCashNotAvailable();
error BorrowNotAllowed();
error RepayBorrowComptrollerRejection(uint256 errorCode);
error RepayBorrowFreshnessCheck();
error LiquidateComptrollerRejection(uint256 errorCode);
error LiquidateFreshnessCheck();
error LiquidateCollateralFreshnessCheck();
error LiquidateAccrueBorrowInterestFailed(uint256 errorCode);
error LiquidateAccrueCollateralInterestFailed(uint256 errorCode);
error LiquidateLiquidatorIsBorrower();
error LiquidateCloseAmountIsZero();
error LiquidateCloseAmountIsUintMax();
error LiquidateRepayBorrowFreshFailed(uint256 errorCode);
error LiquidateSeizeComptrollerRejection(uint256 errorCode);
error LiquidateSeizeLiquidatorIsBorrower();
error AcceptAdminPendingAdminCheck();
error SetComptrollerOwnerCheck();
error SetPendingAdminOwnerCheck();
error SetReserveFactorAdminCheck();
error SetReserveFactorFreshCheck();
error SetReserveFactorBoundsCheck();
error AddReservesFactorFreshCheck(uint256 actualAddAmount);
error ReduceReservesAdminCheck();
error ReduceReservesFreshCheck();
error ReduceReservesCashNotAvailable();
error ReduceReservesCashValidation();
error SetInterestRateModelOwnerCheck();
error SetBorrowRateMaxMantissaOwnerCheck();
error SetInterestRateModelFreshCheck();
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.20;
/**
* @title ERC 20 Token Standard Interface
* https://eips.ethereum.org/EIPS/eip-20
*/
interface EIP20Interface {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
/**
* @notice Get the total number of tokens in circulation
* @return The supply of tokens
*/
function totalSupply() external view returns (uint256);
/**
* @notice Gets the balance of the specified address
* @param owner The address from which the balance will be retrieved
* @return balance The balance
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @notice Transfer `amount` tokens from `msg.sender` to `dst`
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
* @return success Whether or not the transfer succeeded
*/
function transfer(
address dst,
uint256 amount
) external returns (bool success);
/**
* @notice Transfer `amount` tokens from `src` to `dst`
* @param src The address of the source account
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
* @return success Whether or not the transfer succeeded
*/
function transferFrom(
address src,
address dst,
uint256 amount
) external returns (bool success);
/**
* @notice Approve `spender` to transfer up to `amount` from `src`
* @dev This will overwrite the approval amount for `spender`
* and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
* @param spender The address of the account which may transfer tokens
* @param amount The number of tokens that are approved (-1 means infinite)
* @return success Whether or not the approval succeeded
*/
function approve(
address spender,
uint256 amount
) external returns (bool success);
/**
* @notice Get the current allowance from `owner` for `spender`
* @param owner The address of the account which owns the tokens to be spent
* @param spender The address of the account which may transfer tokens
* @return remaining The number of tokens allowed to be spent (-1 means infinite)
*/
function allowance(
address owner,
address spender
) external view returns (uint256 remaining);
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(
address indexed owner,
address indexed spender,
uint256 amount
);
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.20;
/**
* @title Compound's InterestRateModel Interface
* @author Compound
*/
abstract contract InterestRateModel {
/// @notice Indicator that this is an InterestRateModel contract (for inspection)
bool public constant isInterestRateModel = true;
/**
* @notice Calculates the current borrow interest rate per block
* @param cash The total amount of cash the market has
* @param borrows The total amount of borrows the market has outstanding
* @param reserves The total amount of reserves the market has
* @return The borrow rate per block (as a percentage, and scaled by 1e18)
*/
function getBorrowRate(
uint cash,
uint borrows,
uint reserves,
uint deltaTime,
uint _OldfullUtilizationRate
) external view virtual returns (uint, uint);
/**
* @notice Calculates the current supply interest rate per block
* @param cash The total amount of cash the market has
* @param borrows The total amount of borrows the market has outstanding
* @param reserves The total amount of reserves the market has
* @param reserveFactorMantissa The current reserve factor the market has
* @return The supply rate per block (as a percentage, and scaled by 1e18)
*/
function getSupplyRate(
uint cash,
uint borrows,
uint reserves,
uint reserveFactorMantissa,
uint deltaTime,
uint _OldfullUtilizationRate
) external view virtual returns (uint);
function utilizationRate(
uint cash,
uint borrows,
uint reserves
) external pure virtual returns (uint);
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.20;
/**
* @title Exponential module for storing fixed-precision decimals
* @author Compound
* @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.
* Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:
* `Exp({mantissa: 5100000000000000000})`.
*/
contract ExponentialNoError {
uint constant expScale = 1e18;
uint constant doubleScale = 1e36;
uint constant halfExpScale = expScale / 2;
uint constant mantissaOne = expScale;
struct Exp {
uint mantissa;
}
struct Double {
uint mantissa;
}
/**
* @dev Truncates the given exp to a whole number value.
* For example, truncate(Exp{mantissa: 15 * expScale}) = 15
*/
function truncate(Exp memory exp) internal pure returns (uint) {
// Note: We are not using careful math here as we're performing a division that cannot fail
return exp.mantissa / expScale;
}
/**
* @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
*/
function mul_ScalarTruncate(
Exp memory a,
uint scalar
) internal pure returns (uint) {
Exp memory product = mul_(a, scalar);
return truncate(product);
}
/**
* @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.
*/
function mul_ScalarTruncateAddUInt(
Exp memory a,
uint scalar,
uint addend
) internal pure returns (uint) {
Exp memory product = mul_(a, scalar);
return add_(truncate(product), addend);
}
/**
* @dev Checks if first Exp is less than second Exp.
*/
function lessThanExp(
Exp memory left,
Exp memory right
) internal pure returns (bool) {
return left.mantissa < right.mantissa;
}
/**
* @dev Checks if left Exp <= right Exp.
*/
function lessThanOrEqualExp(
Exp memory left,
Exp memory right
) internal pure returns (bool) {
return left.mantissa <= right.mantissa;
}
/**
* @dev Checks if left Exp > right Exp.
*/
function greaterThanExp(
Exp memory left,
Exp memory right
) internal pure returns (bool) {
return left.mantissa > right.mantissa;
}
/**
* @dev returns true if Exp is exactly zero
*/
function isZeroExp(Exp memory value) internal pure returns (bool) {
return value.mantissa == 0;
}
function safe224(
uint n,
string memory errorMessage
) internal pure returns (uint224) {
require(n < 2 ** 224, errorMessage);
return uint224(n);
}
function safe32(
uint n,
string memory errorMessage
) internal pure returns (uint32) {
require(n < 2 ** 32, errorMessage);
return uint32(n);
}
function add_(
Exp memory a,
Exp memory b
) internal pure returns (Exp memory) {
return Exp({mantissa: add_(a.mantissa, b.mantissa)});
}
function add_(
Double memory a,
Double memory b
) internal pure returns (Double memory) {
return Double({mantissa: add_(a.mantissa, b.mantissa)});
}
function add_(uint a, uint b) internal pure returns (uint) {
return a + b;
}
function sub_(
Exp memory a,
Exp memory b
) internal pure returns (Exp memory) {
return Exp({mantissa: sub_(a.mantissa, b.mantissa)});
}
function sub_(
Double memory a,
Double memory b
) internal pure returns (Double memory) {
return Double({mantissa: sub_(a.mantissa, b.mantissa)});
}
function sub_(uint a, uint b) internal pure returns (uint) {
return a - b;
}
function mul_(
Exp memory a,
Exp memory b
) internal pure returns (Exp memory) {
return Exp({mantissa: mul_(a.mantissa, b.mantissa) / expScale});
}
function mul_(Exp memory a, uint b) internal pure returns (Exp memory) {
return Exp({mantissa: mul_(a.mantissa, b)});
}
function mul_(uint a, Exp memory b) internal pure returns (uint) {
return mul_(a, b.mantissa) / expScale;
}
function mul_(
Double memory a,
Double memory b
) internal pure returns (Double memory) {
return Double({mantissa: mul_(a.mantissa, b.mantissa) / doubleScale});
}
function mul_(
Double memory a,
uint b
) internal pure returns (Double memory) {
return Double({mantissa: mul_(a.mantissa, b)});
}
function mul_(uint a, Double memory b) internal pure returns (uint) {
return mul_(a, b.mantissa) / doubleScale;
}
function mul_(uint a, uint b) internal pure returns (uint) {
return a * b;
}
function div_(
Exp memory a,
Exp memory b
) internal pure returns (Exp memory) {
return Exp({mantissa: div_(mul_(a.mantissa, expScale), b.mantissa)});
}
function div_(Exp memory a, uint b) internal pure returns (Exp memory) {
return Exp({mantissa: div_(a.mantissa, b)});
}
function div_(uint a, Exp memory b) internal pure returns (uint) {
return div_(mul_(a, expScale), b.mantissa);
}
function div_(
Double memory a,
Double memory b
) internal pure returns (Double memory) {
return
Double({mantissa: div_(mul_(a.mantissa, doubleScale), b.mantissa)});
}
function div_(
Double memory a,
uint b
) internal pure returns (Double memory) {
return Double({mantissa: div_(a.mantissa, b)});
}
function div_(uint a, Double memory b) internal pure returns (uint) {
return div_(mul_(a, doubleScale), b.mantissa);
}
function div_(uint a, uint b) internal pure returns (uint) {
return a / b;
}
function fraction(uint a, uint b) internal pure returns (Double memory) {
return Double({mantissa: div_(mul_(a, doubleScale), b)});
}
}// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.20;
/**
* @title EIP20NonStandardInterface
* @dev Version of ERC20 with no return values for `transfer` and `transferFrom`
* See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
*/
interface EIP20NonStandardInterface {
/**
* @notice Get the total number of tokens in circulation
* @return The supply of tokens
*/
function totalSupply() external view returns (uint256);
/**
* @notice Gets the balance of the specified address
* @param owner The address from which the balance will be retrieved
* @return balance The balance
*/
function balanceOf(address owner) external view returns (uint256 balance);
///
/// !!!!!!!!!!!!!!
/// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification
/// !!!!!!!!!!!!!!
///
/**
* @notice Transfer `amount` tokens from `msg.sender` to `dst`
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
*/
function transfer(address dst, uint256 amount) external;
///
/// !!!!!!!!!!!!!!
/// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification
/// !!!!!!!!!!!!!!
///
/**
* @notice Transfer `amount` tokens from `src` to `dst`
* @param src The address of the source account
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
*/
function transferFrom(address src, address dst, uint256 amount) external;
/**
* @notice Approve `spender` to transfer up to `amount` from `src`
* @dev This will overwrite the approval amount for `spender`
* and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
* @param spender The address of the account which may transfer tokens
* @param amount The number of tokens that are approved
* @return success Whether or not the approval succeeded
*/
function approve(
address spender,
uint256 amount
) external returns (bool success);
/**
* @notice Get the current allowance from `owner` for `spender`
* @param owner The address of the account which owns the tokens to be spent
* @param spender The address of the account which may transfer tokens
* @return remaining The number of tokens allowed to be spent
*/
function allowance(
address owner,
address spender
) external view returns (uint256 remaining);
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(
address indexed owner,
address indexed spender,
uint256 amount
);
}{
"remappings": [
"@chainlink/=node_modules/@chainlink/",
"@eth-optimism/=node_modules/@chainlink/contracts/node_modules/@eth-optimism/",
"@layerzerolabs/=node_modules/@layerzerolabs/",
"@openzeppelin/=node_modules/@openzeppelin/",
"@uniswap/=node_modules/@uniswap/",
"base64-sol/=node_modules/base64-sol/",
"eth-gas-reporter/=node_modules/eth-gas-reporter/",
"forge-std/=lib/forge-std/src/",
"hardhat/=node_modules/hardhat/",
"uniV3periphery/=node_modules/uniV3periphery/",
"uniswap-v3-periphery-0.8/=node_modules/uniswap-v3-periphery-0.8/"
],
"optimizer": {
"enabled": true,
"runs": 100
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "shanghai",
"viaIR": true,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"underlying_","type":"address"},{"internalType":"contract ComptrollerInterface","name":"comptroller_","type":"address"},{"internalType":"contract InterestRateModel","name":"interestRateModel_","type":"address"},{"internalType":"uint256","name":"initialExchangeRateMantissa_","type":"uint256"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"uint256","name":"fullUtilizationRate_","type":"uint256"},{"internalType":"address payable","name":"admin_","type":"address"},{"internalType":"address","name":"_vault","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AcceptAdminPendingAdminCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualAddAmount","type":"uint256"}],"name":"AddReservesFactorFreshCheck","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"BorrowCashNotAvailable","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"BorrowComptrollerRejection","type":"error"},{"inputs":[],"name":"BorrowFreshnessCheck","type":"error"},{"inputs":[],"name":"BorrowNotAllowed","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"LiquidateAccrueBorrowInterestFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"LiquidateAccrueCollateralInterestFailed","type":"error"},{"inputs":[],"name":"LiquidateCloseAmountIsUintMax","type":"error"},{"inputs":[],"name":"LiquidateCloseAmountIsZero","type":"error"},{"inputs":[],"name":"LiquidateCollateralFreshnessCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"LiquidateComptrollerRejection","type":"error"},{"inputs":[],"name":"LiquidateFreshnessCheck","type":"error"},{"inputs":[],"name":"LiquidateLiquidatorIsBorrower","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"LiquidateRepayBorrowFreshFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"LiquidateSeizeComptrollerRejection","type":"error"},{"inputs":[],"name":"LiquidateSeizeLiquidatorIsBorrower","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"MintComptrollerRejection","type":"error"},{"inputs":[],"name":"MintFreshnessCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"RedeemComptrollerRejection","type":"error"},{"inputs":[],"name":"RedeemFreshnessCheck","type":"error"},{"inputs":[],"name":"RedeemTransferOutNotPossible","type":"error"},{"inputs":[],"name":"ReduceReservesAdminCheck","type":"error"},{"inputs":[],"name":"ReduceReservesCashNotAvailable","type":"error"},{"inputs":[],"name":"ReduceReservesCashValidation","type":"error"},{"inputs":[],"name":"ReduceReservesFreshCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"RepayBorrowComptrollerRejection","type":"error"},{"inputs":[],"name":"RepayBorrowFreshnessCheck","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SetBorrowRateMaxMantissaOwnerCheck","type":"error"},{"inputs":[],"name":"SetComptrollerOwnerCheck","type":"error"},{"inputs":[],"name":"SetInterestRateModelFreshCheck","type":"error"},{"inputs":[],"name":"SetInterestRateModelOwnerCheck","type":"error"},{"inputs":[],"name":"SetPendingAdminOwnerCheck","type":"error"},{"inputs":[],"name":"SetReserveFactorAdminCheck","type":"error"},{"inputs":[],"name":"SetReserveFactorBoundsCheck","type":"error"},{"inputs":[],"name":"SetReserveFactorFreshCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"TransferComptrollerRejection","type":"error"},{"inputs":[],"name":"TransferNotAllowed","type":"error"},{"inputs":[],"name":"TransferNotEnough","type":"error"},{"inputs":[],"name":"TransferTooMuch","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"cashPrior","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestAccumulated","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"AccrueInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"","type":"address"}],"name":"AddedStrategy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract CNumaToken","name":"_collateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"_borrowtorepay","type":"uint256"}],"name":"LeverageClose","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract CNumaToken","name":"_collateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"_suppliedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_borrowAmountVault","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_borrowAmount","type":"uint256"}],"name":"LeverageOpen","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"cTokenCollateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"LiquidateBadDebt","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"cTokenCollateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"LiquidateBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"mintAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintTokens","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract ComptrollerInterface","name":"oldComptroller","type":"address"},{"indexed":false,"internalType":"contract ComptrollerInterface","name":"newComptroller","type":"address"}],"name":"NewComptroller","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract InterestRateModel","name":"oldInterestRateModel","type":"address"},{"indexed":false,"internalType":"contract InterestRateModel","name":"newInterestRateModel","type":"address"}],"name":"NewMarketInterestRateModel","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldPendingAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldReserveFactorMantissa","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"NewReserveFactor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"redeemer","type":"address"},{"indexed":false,"internalType":"uint256","name":"redeemAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"","type":"address"}],"name":"RemovedStrategy","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"RepayBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"benefactor","type":"address"},{"indexed":false,"internalType":"uint256","name":"addAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"reduceAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesReduced","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vaultAddress","type":"address"}],"name":"SetVault","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldFullUtilizationRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFullUtilizationRate","type":"uint256"}],"name":"UpdateRate","type":"event"},{"inputs":[],"name":"NO_ERROR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_acceptAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"addAmount","type":"uint256"}],"name":"_addReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reduceAmount","type":"uint256"}],"name":"_reduceReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_borrowRateMaxMantissa","type":"uint256"}],"name":"_setBorrowRateMaxMantissa","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ComptrollerInterface","name":"newComptroller","type":"address"}],"name":"_setComptroller","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract InterestRateModel","name":"newInterestRateModel","type":"address"}],"name":"_setInterestRateModel","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"newPendingAdmin","type":"address"}],"name":"_setPendingAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"_setReserveFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"accrualBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accrualBlockTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accrueInterest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"addStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_allow","type":"bool"}],"name":"allowReservesTransferToVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOfUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"borrowAmount","type":"uint256"}],"name":"borrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowRateMaxMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract CNumaToken","name":"_collateral","type":"address"},{"internalType":"uint256","name":"_borrowtorepay","type":"uint256"},{"internalType":"uint256","name":"_strategyIndex","type":"uint256"}],"name":"closeLeverageAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract CNumaToken","name":"_collateral","type":"address"},{"internalType":"uint256","name":"_borrowtorepay","type":"uint256"},{"internalType":"uint256","name":"_maxRedeemedAmount","type":"uint256"},{"internalType":"uint256","name":"_strategyIndex","type":"uint256"}],"name":"closeLeverageStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"comptroller","outputs":[{"internalType":"contract ComptrollerInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exchangeRateCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exchangeRateStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fullUtilizationRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountSnapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"_closePosition","type":"bool"},{"internalType":"uint256","name":"_strategyIndex","type":"uint256"}],"name":"getAmountIn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCash","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLeverageStrategies","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ComptrollerInterface","name":"comptroller_","type":"address"},{"internalType":"contract InterestRateModel","name":"interestRateModel_","type":"address"},{"internalType":"uint256","name":"initialExchangeRateMantissa_","type":"uint256"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"uint256","name":"fullUtilizationRate_","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"underlying_","type":"address"},{"internalType":"contract ComptrollerInterface","name":"comptroller_","type":"address"},{"internalType":"contract InterestRateModel","name":"interestRateModel_","type":"address"},{"internalType":"uint256","name":"initialExchangeRateMantissa_","type":"uint256"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"uint256","name":"fullUtilizationRate_","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"interestRateModel","outputs":[{"internalType":"contract InterestRateModel","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isCToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addy","type":"address"}],"name":"isStrategy","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_suppliedAmount","type":"uint256"},{"internalType":"uint256","name":"_borrowAmount","type":"uint256"},{"internalType":"uint256","name":"_maxBorrowAmount","type":"uint256"},{"internalType":"contract CNumaToken","name":"_collateral","type":"address"},{"internalType":"uint256","name":"_strategyIndex","type":"uint256"}],"name":"leverageStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"uint256","name":"percentageToTake","type":"uint256"},{"internalType":"contract CTokenInterface","name":"cTokenCollateral","type":"address"}],"name":"liquidateBadDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"contract CTokenInterface","name":"cTokenCollateral","type":"address"}],"name":"liquidateBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"mintAmount","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolSeizeShareMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemAmount","type":"uint256"}],"name":"redeemUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"removeStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrowBehalf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveFactorMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"liquidator","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"seize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"liquidator","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"seizeBadDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"setVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supplyRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract EIP20NonStandardInterface","name":"token","type":"address"}],"name":"sweepToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBorrows","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBorrowsCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"transferReservesToVault","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"underlying","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"contract INumaVault","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60c0604090808252346200037a57620063bc803803809162000022828562000685565b8339810190610140818303126200037a576200003e81620006bd565b6020828101516001600160a01b0360a0818152909390929091848116908190036200037a57878601519485168095036200037a57606086015160808701519093906001600160401b03908181116200037a57896200009e918a01620006d2565b9886890151908282116200037a57620000b9918a01620006d2565b9060c08901519960ff8b16809b036200037a5760e08a0151986101008b01519a89518c16809c036200037a57610120620000f49101620006bd565b9960049665048c27395000885560018060a01b0319998a60805260059a33908c5416178b55600b5415806200067a575b156200062b578060095515620005cf578e600754815192623f1ee960e11b84528b848c81845afa918215620005a3576200018c8f937f7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d968f5f92620005ad575b505062000761565b81608051821617600755835192511682528b820152a143600b55670de0b6b3a7640000600e5542600c55600d558c6008548151926310c8fc9560e11b845289848a81845afa938415620005a3577fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926946200020f915f916200056f575b5062000761565b806080518316176008558251918c5116825289820152a18051928284116200047c576001938454928584811c9416801562000564575b8985101462000551578190601f94858111620004ff575b5089908583116001146200049b575f926200048f575b50505f19600383901b1c191690851b1784555b80519283116200047c576002548481811c9116801562000471575b888210146200045e579081838997969594931162000406575b50859183116001146200039c575f91908362000390575b50505f19600383901b1c191690821b176002555b60ff19998a6003541617600355895f5416175f558551168060805160155416176015558951928380926318160ddd60e01b82525afa8015620003865762000353575b5050608051938482541617905551169060165416176016556019541660195551615c0d9081620007af8239f35b813d83116200037e575b62000369818362000685565b810103126200037a575f8062000326565b5f80fd5b503d6200035d565b88513d5f823e3d90fd5b015190505f80620002d0565b601f9594939291951982169560025f52855f20915f5b888110620003ef5750838596979810620003d6575b505050811b01600255620002e4565b01515f1960f88460031b161c191690555f8080620003c7565b8183015184558a97938601939283019201620003b2565b9091929394955060025f52875f20838086018b1c8201928a871062000454575b948a989796959493929194018b1c01905b818110620004465750620002b9565b5f8155899750850162000437565b9250819262000426565b602287634e487b7160e01b5f525260245ffd5b90607f1690620002a0565b604186634e487b7160e01b5f525260245ffd5b015190505f8062000272565b90879350601f19831691845f528b5f20925f5b8d828210620004e85750508411620004cf575b505050811b01845562000285565b01515f1960f88460031b161c191690555f8080620004c1565b8385015186558b97909501949384019301620004ae565b909150865f52895f20858085018d1c8201928c861062000547575b918d8a928796959401901c01915b828110620005385750506200025c565b5f815585945089910162000528565b925081926200051a565b602288634e487b7160e01b5f525260245ffd5b93607f169362000245565b6200059491508c8d3d106200059b575b6200058b818362000685565b81019062000747565b5f62000208565b503d6200057f565b83513d5f823e3d90fd5b620005c79250803d106200059b576200058b818362000685565b5f8f62000184565b8e5162461bcd60e51b81528089018a9052603060248201527f696e697469616c2065786368616e67652072617465206d75737420626520677260448201526f32b0ba32b9103a3430b7103d32b9379760811b6064820152608490fd5b8f5162461bcd60e51b8152808a018b9052602360248201527f6d61726b6574206d6179206f6e6c7920626520696e697469616c697a6564206f6044820152626e636560e81b6064820152608490fd5b50600e541562000124565b601f909101601f19168101906001600160401b03821190821017620006a957604052565b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036200037a57565b919080601f840112156200037a578251906001600160401b038211620006a957604051916020916200070e601f8301601f191684018562000685565b8184528282870101116200037a575f5b818110620007335750825f9394955001015290565b85810183015184820184015282016200071e565b908160209103126200037a575180151581036200037a5790565b156200076957565b60405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c7365000000006044820152606490fdfe60806040526004361015610011575f80fd5b5f3560e01c806302bd443f1461045f57806306fdde031461045a578063095ea7b3146104555780630e75270214610450578063173b99041461044b578063175188e81461044657806317bfdfbc1461044157806318160ddd1461043c578063182df0f5146104375780631be1956014610432578063223e54791461042d57806323b872dd146104285780632608f81814610423578063267822471461041e5780632e8ebaae14610419578063313ce567146104145780633a76de711461040f5780633af9e6691461040a5780633b1d21a2146104055780633c7a3877146104005780633d5d1d99146103fb5780633e941010146103f65780634576b5db146103f157806347bd3718146103ec5780635fe3b567146103e7578063601a0bf1146103e25780636752e702146103d85780636817031b146103dd57806369ab3250146103d85780636c540baf146103d35780636f307dc3146103ce57806370a08231146103c957806372a4b3ba146103c457806373acee98146103bf578063852a12e3146103ba57806385d89d93146103b5578063889bcdc6146103b05780638ce10c09146103ab5780638f840ddd146103a657806395d89b41146103a157806395dd91931461039c57806396eb171e146103975780639af6702e14610392578063a0712d681461038d578063a6afed9514610388578063a9059cbb14610383578063aa5af0fd1461037e578063ae9d70b014610379578063b2a02ff114610374578063b71d1a0c1461036f578063bd6d894d1461036a578063c37f68e214610365578063c5ebeaec14610360578063cfa992011461035b578063db006a7514610356578063dd62ed3e14610351578063e4e62c581461034c578063e9c714f214610347578063ee27a2f214610342578063f2b3abbd1461033d578063f331b0ec14610338578063f3fdb15a14610333578063f5e3c4621461032e578063f851a44014610329578063f8f9da2814610324578063f9319ff11461031f578063fbfa77cf1461031a578063fca7820b146103155763fe9c44ae14610310575f80fd5b6131a6565b6130c5565b61309d565b61305e565b612f25565b612efd565b612e04565b612ddc565b612dbf565b612d8f565b612d72565b612c62565b612c25565b612bc6565b612b15565b612af8565b612aaf565b612a4d565b612a33565b612997565b612963565b61282f565b612812565b6127cc565b6127b2565b6125aa565b61251a565b611efc565b611ed5565b611e32565b611e15565b611d5f565b611c78565b611bd9565b6118ba565b611873565b61182f565b6117f4565b6117cc565b6117af565b611720565b61173a565b611602565b6115da565b6115bd565b611596565b6114d5565b611454565b610cc5565b610cab565b610c42565b610c20565b610c00565b610bb8565b610b90565b610b44565b610afe565b6109d6565b6108c5565b6108a3565b610886565b61082e565b610772565b610755565b610705565b61067a565b6105a9565b610472565b8015150361046e57565b5f80fd5b3461046e57602036600319011261046e5760043561048f81610464565b6104a460018060a01b0360055416331461332f565b60ff80196019541691151516176019555f80f35b5f91031261046e57565b90600182811c921680156104f0575b60208310146104dc57565b634e487b7160e01b5f52602260045260245ffd5b91607f16916104d1565b634e487b7160e01b5f52604160045260245ffd5b6001600160401b03811161052157604052565b6104fa565b602081019081106001600160401b0382111761052157604052565b90601f801991011681019081106001600160401b0382111761052157604052565b602080825282518183018190529093925f5b82811061059557505060409293505f838284010152601f8019910116010190565b818101860151848201604001528501610574565b3461046e575f80600319360112610666576040518160018054906105cc826104c2565b8085529181811690811561063e5750600114610603575b6105ff846105f381880382610541565b60405191829182610562565b0390f35b80945082526020938483205b82841061062b57505050816105ff936105f392820101936105e3565b805485850187015292850192810161060f565b6105ff96506105f39450602092508593915060ff191682840152151560051b820101936105e3565b80fd5b6001600160a01b0381160361046e57565b3461046e57604036600319011261046e5760043561069781610669565b60243590335f526013602052816106c18260405f209060018060a01b03165f5260205260405f2090565b556040519182526001600160a01b03169033907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3602060405160018152f35b3461046e57602036600319011261046e5760015f5461072660ff8216613ad3565b60ff199081165f55610736613460565b50610744600435333361380c565b505f5416175f5560206040515f8152f35b3461046e575f36600319011261046e576020600a54604051908152f35b3461046e57602036600319011261046e5760043561078f81610669565b6005546001600160a01b0391906107a9908316331461332f565b166107bf815f52601860205260405f2054151590565b156107fb576020816107f17f5a77cb189e9fdbd0240874f9dff8b513450c6d543ff335a7e748ccf3623be5ee93615ade565b50604051908152a1005b60405162461bcd60e51b815260206004820152600b60248201526a1b9bdd081a5b881b1a5cdd60aa1b6044820152606490fd5b3461046e57602036600319011261046e57602060043561084d81610669565b60016108775f549261086160ff8516613ad3565b60ff199384165f55610871613460565b50614712565b915f5416175f55604051908152f35b3461046e575f36600319011261046e576020601154604051908152f35b3461046e575f36600319011261046e5760206108bd61466b565b604051908152f35b3461046e57602036600319011261046e576004356108e281610669565b6005546001600160a01b03908116916108fc338414613249565b6015549116919061092990610921906001600160a01b03165b6001600160a01b031690565b8314156132ad565b6040516370a0823160e01b8152306004820152602081602481865afa9081156109a3575f916109a8575b50823b1561046e5761097e925f928360405180968195829463a9059cbb60e01b845260048401613314565b03925af180156109a35761098e57005b8061099b6109a19261050e565b806104b8565b005b6131d0565b6109c9915060203d81116109cf575b6109c18183610541565b8101906131c1565b5f610953565b503d6109b7565b3461046e57602036600319011261046e576004356109f381610669565b6005546001600160a01b0390610a0c908216331461332f565b600a6017541015610a9757610a22908216615a2e565b15610a60576040516001600160a01b039190911681527f427f3dac4018b5a78b7d3e92b04e7cd4693fc9f4d7b1e5711f803598cd9adb9d90602090a1005b60405162461bcd60e51b815260206004820152600f60248201526e185b1c9958591e481a5b881b1a5cdd608a1b6044820152606490fd5b60405162461bcd60e51b8152602060048201526013602482015272746f6f206d616e79207374726174656769657360681b6044820152606490fd5b606090600319011261046e57600435610aea81610669565b90602435610af781610669565b9060443590565b3461046e576020610b346001610b1336610ad2565b905f9492945494610b2660ff8716613ad3565b60ff199586165f5533614472565b15915f5416175f55604051908152f35b3461046e57604036600319011261046e576001600435610b6381610669565b6107445f5491610b7560ff8416613ad3565b60ff199283165f55610b85613460565b50602435903361380c565b3461046e575f36600319011261046e576006546040516001600160a01b039091168152602090f35b3461046e57602036600319011261046e576020610bf6600435610bda81610669565b6001600160a01b03165f90815260186020526040902054151590565b6040519015158152f35b3461046e575f36600319011261046e57602060ff60035416604051908152f35b3461046e575f36600319011261046e57602060ff601954166040519015158152f35b3461046e57602036600319011261046e576020670de0b6b3a7640000610ca1600435610c6d81610669565b610c75614752565b9060405191610c8383610526565b82526001600160a01b03165f90815260128552604090205490615833565b5104604051908152f35b3461046e575f36600319011261046e5760206108bd6133bc565b3461046e5760a036600319011261046e5760643560046024358135610ce984610669565b601654610cfe906001600160a01b0316610915565b92604094855163980fa2ad60e01b91828252602092838386818b5afa9283156109a3575f936113c5575b506001600160a01b03978893841630149182611373575b82156112c7575b5050610d5190613722565b610d59613460565b501695805163a6afed9560e01b8152828185815f8c5af180156109a3576112aa575b50610d90610915610915610915608435615b83565b8151636f307dc360e01b8152838186818c5afa9081156109a3575f9161127d575b50601654610dc7906001600160a01b0316610915565b90813b1561046e5783516309a5abaf60e11b81528681018981525f602082018190529193849182908490829060400103925af19182156109a357899261126a575b501696610e178630338b6158c7565b610e218787613399565b83516370a0823160e01b808252308883019081529194929187908690819003602001818f5afa9182156109a3578b888e8b985f9661124b575b50895192838080938763095ea7b360e01b9d8e8452830191610e7b92613314565b03915a905f91f19182156109a3578e8a92610eb69461122e575b508b5f8b5180968195829463140e25ad60e31b845283019190602083019252565b03925af180156109a357611211575b5085519081523088820190815287908290819003602001818f5afa9182156109a357610f1792610f10925f916111f4575b50610f0b610f04838361338c565b1515613778565b61338c565b338c61588d565b16968251936301c48f3b60e31b8552808580610f428b8a830160205f91939293604081019481520152565b03818c5afa9485156109a3575f956111d5575b50610f646044358611156137b4565b610f9985610f93610f7433614712565b610f7e3384613b0c565b335f9081526014602052604090205b5461338c565b146137d4565b610fcb8186610fb561091561091560155460018060a01b031690565b8c5f8b8a51968795869485938d85528401613314565b03925af180156109a3576110129a86928b926111b8575b50875f8a85519e8f95869485936315f6307f60e11b855284016040905f9294936060820195825260208201520152565b03925af19283156109a3575f998a94611180575b5060165489918391611040906001600160a01b0316610915565b906110558851948593849384528c8401613314565b03815f875af180156109a357611152575b505060165461107d906001600160a01b0316610915565b803b1561046e578351630164e84f60e51b81525f968101878152909691879182908490829060200103925af19081156109a3577f242797a9cf3b15d6da47c6fb21848e30c43b12d06aab0880c8c9c8af51e4201c9861110b96899361113f575b50828111611122575b50505080611110575b5051938493846040919493926060820195825260208201520152565b0390a2005b61111b90333061380c565b505f6110ef565b6111379261112f9161338c565b90339061588d565b855f806110e6565b8061099b61114c9261050e565b5f6110dd565b8161117192903d10611179575b6111698183610541565b810190613763565b505f80611066565b503d61115f565b829a50899194506111a690863d88116111b1575b61119e8183610541565b8101906133a6565b9a909a949150611026565b503d611194565b6111ce90853d8711611179576111698183610541565b505f610fe2565b816111ed9296503d87116109cf576109c18183610541565b935f610f55565b61120b9150893d8b116109cf576109c18183610541565b5f610ef6565b61122790883d8a116109cf576109c18183610541565b505f610ec5565b61124490843d8611611179576111698183610541565b505f610e95565b611263919650823d84116109cf576109c18183610541565b945f610e5a565b8061099b6112779261050e565b5f610e08565b61129d9150843d86116112a3575b6112958183610541565b81019061370d565b5f610db1565b503d61128b565b6112c090833d85116109cf576109c18183610541565b505f610d7b565b8a516305a7337560e51b81529450915084848781845afa80156109a35789869181965f91611356575b501630149283611307575b505050610d515f610d46565b9194509150858a518095819382525afa80156109a35787610d519181945f91611339575b50168383161490845f6112fb565b6113509150863d88116112a3576112958183610541565b5f61132b565b61136d9150833d85116112a3576112958183610541565b5f6112f0565b8a516305a7337560e51b81529450915084848781845afa9384156109a357899485915f916113a8575b50168484161491610d3f565b6113bf9150873d89116112a3576112958183610541565b5f61139c565b6113dd919350843d86116112a3576112958183610541565b915f610d28565b604051906113f182610526565b565b6001600160401b03811161052157601f01601f191660200190565b81601f8201121561046e57803590611425826113f3565b926114336040519485610541565b8284526020838301011161046e57815f926020809301838601378301015290565b3461046e5760e036600319011261046e5760043561147181610669565b60243561147d81610669565b6001600160401b0360643581811161046e5761149d90369060040161140e565b9060843590811161046e576114b690369060040161140e565b60a4359160ff8316830361046e576109a19460c435946044359161432c565b3461046e57602036600319011261046e575f546114f460ff8216613ad3565b60ff19165f55611502613460565b50600b54430361157e57611518600435336147cc565b601054818101809111611579577fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc5918160105561155b6040519283923384613979565b0390a161156e600160ff195f5416175f55565b6040515f8152602090f35b613368565b6040516338acf79960e01b81525f6004820152602490fd5b3461046e57602036600319011261046e5760206108bd6004356115b881610669565b615689565b3461046e575f36600319011261046e576020600f54604051908152f35b3461046e575f36600319011261046e576007546040516001600160a01b039091168152602090f35b3461046e57602036600319011261046e576004355f5461162460ff8216613ad3565b60ff19165f55611632613460565b50600554611648906001600160a01b0316610915565b330361170e57600b5443036116fc57806116606133bc565b106116ea57601054908181116116d85761169b817f3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e9361338c565b6116a481601055565b6005546116bb9083906001600160a01b0316613a0e565b60055461155b906001600160a01b03169160405193849384613979565b6040516378d2980560e11b8152600490fd5b604051633345e99960e01b8152600490fd5b604051630dff50cb60e41b8152600490fd5b604051630f7e5e6d60e41b8152600490fd5b3461046e575f36600319011261046e5760206040515f8152f35b3461046e57602036600319011261046e577fd459c7242e23d490831b5676a611c4342d899d28f342d89ae80793e56a930f30602060043561177a81610669565b6005546001600160a01b039190611794908316331461332f565b168060018060a01b03196016541617601655604051908152a1005b3461046e575f36600319011261046e576020600b54604051908152f35b3461046e575f36600319011261046e576015546040516001600160a01b039091168152602090f35b3461046e57602036600319011261046e5760043561181181610669565b60018060a01b03165f526012602052602060405f2054604051908152f35b3461046e57600161186361184236610ad2565b905f939293549361185560ff8616613ad3565b60ff199485165f5533615520565b5f5416175f5560206040515f8152f35b3461046e575f8060031936011261066657805460209161189560ff8316613ad3565b60ff1991821681556118a5613460565b506001600f5492825416179055604051908152f35b3461046e5760208060031936011261046e575f805460048035906118e060ff8416613ad3565b60ff1992831684556118f0613460565b5061190d6118fc61466b565b6119046113e4565b90815283615868565b600754909290611925906001600160a01b0316610915565b95604096818851809263eabe7d9160e01b8252818a816119498b33308d8501613b47565b03925af19081156109a3578791611bbc575b5080611b9c5750600b544303611b8c57816119746133bc565b10611a8e575b5061198f61198a8460115461338c565b601155565b61199c83610f8d336137bb565b6119a5336137bb565b556119b08133613a0e565b8551838152309033905f80516020615bb883398151915290602090a37fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a9298651806119fc86853384613979565b0390a1600754611a14906001600160a01b0316610915565b91823b15611a8a5786516351dff98960e01b8152309181019182523360208301526040820192909252606081019390935292918491849182908490829060800103925af19182156109a357600192611a77575b50825416179055515f8152602090f35b8061099b611a849261050e565b5f611a67565b8580fd5b611a9f611a996133bc565b8361338c565b6016548790611ab6906001600160a01b0316610915565b926001600160a01b038416611b32575b508111611b2257813b15611b1e57875163317afabb60e21b815284810191825291879183919082908490829060200103925af180156109a357611b0b575b505f61197a565b8061099b611b189261050e565b5f611b04565b8680fd5b87516391240a1b60e01b81528490fd5b9050885163cbb575bf60e01b8152818180611b54898201906001602083019252565b0381875afa9182156109a3578992611b6f575b50505f611ac6565b611b859250803d106109cf576109c18183610541565b5f80611b67565b86516397b5cfcd60e01b81528390fd5b875163480f424760e01b81528085019182529081906020010390fd5b0390fd5b611bd39150823d84116109cf576109c18183610541565b5f61195b565b3461046e575f8060031936011261066657604051809160175490818352602080930180926017835284832090835b818110611c645750505084611c1d910385610541565b60405193838594850191818652518092526040850193925b828110611c4457505050500390f35b83516001600160a01b031685528695509381019392810192600101611c35565b825484529286019260019283019201611c07565b3461046e57608036600319011261046e575f600435611c9681610669565b60643590611ca382610669565b602060018060a01b03611cbb81601654163314613f2c565b8454611cc960ff8216613ad3565b60ff19168555611cd7613460565b5060046040518096819363a6afed9560e01b835287165af19283156109a3575f93611d3f575b5082611d2657611d16925060443590602435903361506a565b61156e600160ff195f5416175f55565b604051633eea49b760e11b815260048101849052602490fd5b611d5891935060203d81116109cf576109c18183610541565b915f611cfd565b3461046e57606036600319011261046e57611dd16020602435611d8181610464565b611da4611d8f604435615a11565b905460039190911b1c6001600160a01b031690565b6040516301c48f3b60e31b8152600480359082015291151560248301529092839190829081906044820190565b03915afa80156109a3576105ff915f91611df7575b506040519081529081906020820190565b611e0f915060203d81116109cf576109c18183610541565b5f611de6565b3461046e575f36600319011261046e576020601054604051908152f35b3461046e575f806003193601126106665760405181600254611e53816104c2565b8084529060019081811690811561063e5750600114611e7c576105ff846105f381880382610541565b60028352602094507f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace5b828410611ec257505050816105ff936105f392820101936105e3565b8054858501870152928501928101611ea6565b3461046e57602036600319011261046e5760206108bd600435611ef781610669565b614712565b3461046e57608036600319011261046e576004803590611f1b82610669565b60165460643590602435908190611f3a906001600160a01b0316610915565b60409182519163980fa2ad60e01b9081845260209182858a81855afa9485156109a3575f956124fb575b506001600160a01b03948516301490816124ad575b8115612404575b50611f8b9150613722565b611f93613460565b50835163a6afed9560e01b8152888416989082818a815f8e5af180156109a3576123e7575b5087611fcc6109156109156109158b615b83565b93838b885193848092636f307dc360e01b82525afa9182156109a3575f926123c8575b50611ff933614712565b8091116123c0575b50601654612017906001600160a01b0316610915565b803b1561046e5786516309a5abaf60e11b8152808b018981526001602082015290915f9183919082908490829060400103925af19889156109a3578887946120749361208c9c6123ad575b5061206e82333061380c565b50613e0e565b98906120846044358b11156137b4565b30338d6158c7565b85516370a0823160e01b808252308b830190815292909316959294918490829081906020010381895afa9081156109a3575f91612390575b50865163852a12e360e01b8152808b018a8152859082908e9082905f90829060200103925af180156109a357612373575b508651948552308a86019081528490869081906020010381895afa80156109a35761212e612135928b928d985f91612356575b5061338c565b1015613eec565b168451908282806121548b8563095ea7b360e01b998a85528401613314565b03815f895af19081156109a35761219e9887938993612339575b505f8b85519b8c95869485936315f6307f60e11b8552840160409060019294936060820195825260208201520152565b03925af19182156109a3575f968793612311575b50601554869183916121ce90610915906001600160a01b031681565b6016548b905f906121e7906001600160a01b0316610915565b936121fd8b519788968795869485528401613314565b03925af180156109a3576122f3575b5050601654612223906001600160a01b0316610915565b803b1561046e578351630164e84f60e51b81526001978101978852965f91889182908490829060200103925af19485156109a3577f5f6909a82162596722a5e14496b5ca56b573b7c08a42b4182bf7622b5a65e3189661110b966122e0575b508481116122b4575b50806122a2575b5050519081529081906020820190565b6122ad91339061588d565b5f80612292565b6015546122da919061112f9087906122d4906001600160a01b0316610915565b9261338c565b5f61228b565b8061099b6122ed9261050e565b5f612282565b8161230992903d10611179576111698183610541565b505f8061220c565b8297508691935061232e90863d88116111b15761119e8183610541565b9790979391506121b2565b61234f90863d8811611179576111698183610541565b505f61216e565b61236d9150883d8a116109cf576109c18183610541565b5f612128565b61238990853d87116109cf576109c18183610541565b505f6120f5565b6123a79150843d86116109cf576109c18183610541565b5f6120c4565b8061099b6123ba9261050e565b5f612062565b96505f612001565b6123e0919250843d86116112a3576112958183610541565b905f611fef565b6123fd90833d85116109cf576109c18183610541565b505f611fb8565b86516305a7337560e51b815290915083818b81865afa9081156109a357849187915f91612490575b501630149182612444575b5050611f8b91505f611f80565b9091508987518094819382525afa9081156109a357611f8b9185915f91612473575b5016848a1614825f612437565b61248a9150843d86116112a3576112958183610541565b5f612466565b6124a79150833d85116112a3576112958183610541565b5f61242c565b86516305a7337560e51b815290915083818b81865afa80156109a35786915f916124de575b5016858b161490611f79565b6124f59150853d87116112a3576112958183610541565b5f6124d2565b612513919550833d85116112a3576112958183610541565b935f611f64565b3461046e5761010036600319011261046e5760043561253881610669565b60243561254481610669565b6044359161255183610669565b6001600160401b0360843581811161046e5761257190369060040161140e565b9060a43590811161046e5761258a90369060040161140e565b9060c4359260ff8416840361046e576109a19560e43595606435926131db565b3461046e57602036600319011261046e576004355f546125cc60ff8216613ad3565b60ff19165f556125da613460565b506007546125f0906001600160a01b0316610915565b90604091602083518092634ef4c3e160e01b8252815f8161261688333060048501613b47565b03925af19081156109a3575f91612794575b508061277b5750600b54430361276a5761265f61265861264661466b565b9261264f6113e4565b938452336147cc565b9182615868565b90601154156126ff575b817f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f9161269b61198a83601154613399565b6126ae826126a8336137bb565b54613399565b6126b7336137bb565b556126c785519283923384613979565b0390a18151908152339030905f80516020615bb883398151915290602090a36126f6600160ff195f5416175f55565b515f8152602090f35b906127627f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f916127306103e8601155565b5f805260126020526103e87f7e7fa33969761a458e04f477e039a608702b4f924981d6653935a8319a08ad7b5561337c565b919050612669565b81516338d8859760e01b8152600490fd5b82516349abd4fd60e01b81526004810191909152602490fd5b6127ac915060203d81116109cf576109c18183610541565b5f612628565b3461046e575f36600319011261046e5760206108bd613460565b3461046e57604036600319011261046e5760206004356127eb81610669565b6001610b345f54926127ff60ff8516613ad3565b60ff199384165f55602435903380614472565b3461046e575f36600319011261046e576020600e54604051908152f35b3461046e575f80600319360112610666576016548190612857906001600160a01b0316610915565b6001600160a01b03811661290f575b506020612875600c544261338c565b60085490929061289f90612891906001600160a01b0316610915565b9161289a6133bc565b613399565b600f54601054600a54600d546040516333d7bb6560e01b81526004810195909552602485019390935260448401919091526064830152608482019490945260a4810193909352829060c49082905afa9081156109a3576105ff9291611df757506040519081529081906020820190565b60405163cbb575bf60e01b81525f60048201529150602090829060249082905afa9081156109a3578291612945575b505f612866565b61295d915060203d81116109cf576109c18183610541565b5f61293e565b3461046e57600161186361297636610ad2565b905f939293549361298960ff8616613ad3565b60ff199485165f553361536e565b3461046e57602036600319011261046e576004356129b481610669565b6005546001600160a01b039081163303612a2157600680546001600160a01b03198116838516179091556040517fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a99390928392612a1392911683615623565b0390a16040515f8152602090f35b604051635cb56c2b60e01b8152600490fd5b3461046e575f36600319011261046e5760206108bd614752565b3461046e57602036600319011261046e576080600435612a6c81610669565b6001600160a01b0381165f9081526012602052604090205490612a8e90614712565b612a9661466b565b90604051925f8452602084015260408301526060820152f35b3461046e57602036600319011261046e5760016004356118635f5491612ad760ff8416613ad3565b60ff199283165f55612ae7613460565b50612af28133613b69565b33613a0e565b3461046e575f36600319011261046e576020600c54604051908152f35b3461046e5760208060031936011261046e575f80546004803590612b3b60ff8416613ad3565b60ff199283168455612b4b613460565b5081159182159283612bbe575b612b61906148fa565b612b6961466b565b92612b726113e4565b93845215612bac57612b8881612b979294615833565b670de0b6b3a764000090510490565b600754611925906001600160a01b0316610915565b5090612bb79061585e565b9083612b97565b506001612b58565b3461046e57604036600319011261046e576020612c1c600435612be881610669565b60243590612bf582610669565b60018060a01b03165f526013835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b3461046e57602036600319011261046e576005546001600160a01b03163303612c5057600480359055005b6040516346e71bad60e11b8152600490fd5b3461046e575f36600319011261046e576006546001600160a01b031680338114801590612d6a575b612d58576005547fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9927ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc91612d0d90612ceb906001600160a01b0316610915565b600580546001600160a01b0319166001600160a01b0390941693909317909255565b600680546001600160a01b03191690556005546001600160a01b031690612d3960405192839283615623565b0390a16006546001600160a01b031690612a1360405192839283615623565b604051631ba24f2960e21b8152600490fd5b503315612c8a565b3461046e575f36600319011261046e576020600454604051908152f35b3461046e57602036600319011261046e5760206108bd600435612db181610669565b612db9613460565b50615765565b3461046e575f36600319011261046e576020600d54604051908152f35b3461046e575f36600319011261046e576008546040516001600160a01b039091168152602090f35b3461046e57606036600319011261046e57600435612e2181610669565b60443590612e2e82610669565b5f602060018060a01b03612e4781601654163314613f2c565b8254612e5560ff8216613ad3565b60ff19168355612e63613460565b5060046040518094819363a6afed9560e01b835288165af19081156109a3575f91612edf575b5080612ec7576105ff612ea0846024358533614c5b565b612eb0600160ff195f5416175f55565b604051918291829190602060408401935f81520152565b60249060405190633eea49b760e11b82526004820152fd5b612ef7915060203d81116109cf576109c18183610541565b5f612e89565b3461046e575f36600319011261046e576005546040516001600160a01b039091168152602090f35b3461046e575f80600319360112610666576016548190612f4d906001600160a01b0316610915565b6001600160a01b03811661300a575b506040612f6b600c544261338c565b600854909290612f8790612891906001600160a01b0316610915565b600f54601054600d54855163108e627960e21b815260048101949094526024840192909252604483015260648201949094526084810193909352829060a49082905afa9081156109a3576105ff9291612feb57506040519081529081906020820190565b613003915060403d81116111b15761119e8183610541565b505f611de6565b60405163cbb575bf60e01b81525f60048201529150602090829060249082905afa9081156109a3578291613040575b505f612f5c565b613058915060203d81116109cf576109c18183610541565b5f613039565b3461046e57606036600319011261046e5761308b60043561307e81610669565b6044359060243590613e0e565b60408051928352602083019190915290f35b3461046e575f36600319011261046e576016546040516001600160a01b039091168152602090f35b3461046e57602036600319011261046e576004355f546130e760ff8216613ad3565b60ff19165f556130f5613460565b5060055461310b906001600160a01b0316610915565b330361319457600b54430361318257670de0b6b3a76400008111613170577faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f82146090600a5461315782600a55565b604080519182526020820192909252908190810161155b565b60405163717220f360e11b8152600490fd5b604051637dfca6b760e11b8152600490fd5b604051631205b57b60e11b8152600490fd5b3461046e575f36600319011261046e57602060405160018152f35b9081602091031261046e575190565b6040513d5f823e3d90fd5b94909360049793602097936131ef9661432c565b601580546001600160a01b0319166001600160a01b039290921691821790556040516318160ddd60e01b815292839182905afa80156109a35761322f5750565b6132469060203d81116109cf576109c18183610541565b50565b1561325057565b60405162461bcd60e51b815260206004820152602f60248201527f4345726332303a3a7377656570546f6b656e3a206f6e6c792061646d696e206360448201526e616e20737765657020746f6b656e7360881b6064820152608490fd5b156132b457565b60405162461bcd60e51b815260206004820152603260248201527f4345726332303a3a7377656570546f6b656e3a2063616e206e6f74207377656560448201527138103ab73232b9363cb4b733903a37b5b2b760711b6064820152608490fd5b6001600160a01b039091168152602081019190915260400190565b1561333657565b60405162461bcd60e51b815260206004820152600a60248201526937b7363c9030b236b4b760b11b6044820152606490fd5b634e487b7160e01b5f52601160045260245ffd5b6103e71981019190821161157957565b9190820391821161157957565b9190820180921161157957565b919082604091031261046e576020825192015190565b6015546040516370a0823160e01b815230600482015290602090829060249082906001600160a01b03165afa9081156109a3575f916133f9575090565b613411915060203d81116109cf576109c18183610541565b90565b1561341b57565b60405162461bcd60e51b815260206004820152601c60248201527f626f72726f772072617465206973206162737572646c792068696768000000006044820152606490fd5b600b54438114613708576016545f9190613482906001600160a01b0316610915565b6001600160a01b0381166136ad575b506134a16135179261289a6133bc565b600f54601054600e546008549294926134c2906001600160a01b0316610915565b6134ce600c544261338c565b600d546040805163108e627960e21b815260048101899052602481018a9052604481018790526064810193909352608483019190915297909188918391908290819060a4820190565b03915afa9384156109a3575f91829561368a575b5090829160045482111561353e90613414565b613548904361338c565b6135506113e4565b91825261355c91615833565b6135668782615833565b51670de0b6b3a764000090049661357d9088613399565b93600a548861358a6113e4565b91825261359691615833565b51670de0b6b3a76400009004906135ac91613399565b926135b691615833565b51670de0b6b3a76400009004906135cc91613399565b43600b559042600c556135de82600e55565b6135e783600f55565b601055600d54958387141596613641947f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc0498613648575b505051948594859094939260609260808301968352602083015260408201520152565b0390a15f90565b825191825260208201819052613683917ffe3b4c3f6e2efdc3dd612c3eb2817308b7568ad72d3f9c9200a08a069c27dff890604090a1600d55565b5f8061361e565b6136a5919550839250883d8a116111b15761119e8183610541565b94909161352b565b60405163cbb575bf60e01b81525f60048201529290602090849060249082905afa80156109a357613517936134a192916136ea575b509250613491565b613702915060203d81116109cf576109c18183610541565b5f6136e2565b505f90565b9081602091031261046e575161341181610669565b1561372957565b60405162461bcd60e51b81526020600482015260126024820152711a5b9d985b1a590818dbdb1b185d195c985b60721b6044820152606490fd5b9081602091031261046e575161341181610464565b1561377f57565b60405162461bcd60e51b815260206004820152600d60248201526c1b9bc818dbdb1b185d195c985b609a1b6044820152606490fd5b1561046e57565b6001600160a01b03165f90815260126020526040902090565b156137db57565b60405162461bcd60e51b8152602060048201526009602482015268626f72726f77206b6f60b81b6044820152606490fd5b906138179291614963565b60165461382c906001600160a01b0316610915565b6040516314a6bf0f60e01b8152906020908183600481845afa9283156109a3575f9361395a575b508261387c575b50505061386960195460ff1690565b6138705790565b613878613997565b5090565b82841115613952575b81836138c0926138a261091561091560155460018060a01b031690565b905f60405180968195829463095ea7b360e01b845260048401613314565b03925af180156109a357613934575b50506016546138e6906001600160a01b0316610915565b803b1561046e57604051631b8fec7360e11b815260048101929092525f908290602490829084905af180156109a357613921575b808061385a565b8061099b61392e9261050e565b5f61391a565b8161394a92903d10611179576111698183610541565b505f806138cf565b839250613885565b613972919350823d84116109cf576109c18183610541565b915f613853565b604091949392606082019560018060a01b0316825260208201520152565b61399f6133bc565b6010548091818110613a06575b5080820391821161157957817f3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e9260105561364160018060a01b036139f5848260165416613a0e565b601654169160405193849384613979565b90505f6139ac565b919060018060a01b036015541690813b1561046e57613a48936040519283809263a9059cbb60e01b8252815f988996879360048401613314565b03925af180156109a357613ac4575b503d8015613aba57602014613a6a575080fd5b90602081803e515b15613a7957565b60405162461bcd60e51b81526020600482015260196024820152781513d2d15397d514905394d1915497d3d55517d19052531151603a1b6044820152606490fd5b5090505f19613a72565b613acd9061050e565b5f613a57565b15613ada57565b60405162461bcd60e51b815260206004820152600a6024820152691c994b595b9d195c995960b21b6044820152606490fd5b90613b3f6001925f5492613b2260ff8516613ad3565b60ff199384165f55613b32613460565b50848060a01b0316613b69565b5f5416175f55565b6001600160a01b03918216815291166020820152604081019190915260600190565b60075490929190613b82906001600160a01b0316610915565b6040805163368f515360e21b81526004959290602090818180613ba98989308e8501613b47565b03815f80975af19081156109a3578391613df1575b5080613dd55750600b544303613dc557613bd66133bc565b858110613c9f575b509495507f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab809450613c9a919050613c218461289a6001600160a01b038616614712565b613c2d85600f54613399565b9181613c498660018060a01b03165f52601460205260405f2090565b55600e546001600160a01b0386165f90815260146020526040902060010155613c7183600f55565b519485948590949392606092608083019660018060a01b03168352602083015260408201520152565b0390a1565b601654613cb4906001600160a01b0316610915565b906001600160a01b03821615613db557613cce908761338c565b91845163cbb575bf60e01b8152818180613cef8d8201906001602083019252565b0381865afa9182156109a3578592613d98575b50508211613d8857803b15613d8457835163317afabb60e21b8152978801918252959695869182908490829060200103925af19081156109a3577f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab8094613c9a92613d71575b509085945f613bde565b8061099b613d7e9261050e565b5f613d67565b8280fd5b83516348c2588160e01b81528890fd5b613dae9250803d106109cf576109c18183610541565b5f80613d02565b84516348c2588160e01b81528990fd5b8251630e8d8c6160e21b81528790fd5b835163918db40f60e01b81528089019182529081906020010390fd5b613e089150823d84116109cf576109c18183610541565b5f613bbe565b613e28610915610915610915610915613e56969897615b83565b6040516301c48f3b60e31b815260048101959095526001602486015260209283918691829081906044820190565b03915afa9384156109a3575f94613ec8575b5060405163182df0f560e01b8152908290829060049082906001600160a01b03165afa9081156109a357613ea7925f92613eab575b50506119046113e4565b9190565b613ec19250803d106109cf576109c18183610541565b5f80613e9d565b6004919450613ee48391823d84116109cf576109c18183610541565b949150613e68565b15613ef357565b60405162461bcd60e51b81526020600482015260116024820152706e6f7420656e6f7567682072656465656d60781b6044820152606490fd5b15613f3357565b60405162461bcd60e51b815260206004820152600a6024820152697661756c74206f6e6c7960b01b6044820152606490fd5b15613f6c57565b60405162461bcd60e51b815260206004820152602360248201527f6d61726b6574206d6179206f6e6c7920626520696e697469616c697a6564206f6044820152626e636560e81b6064820152608490fd5b15613fc457565b60405162461bcd60e51b815260206004820152603060248201527f696e697469616c2065786368616e67652072617465206d75737420626520677260448201526f32b0ba32b9103a3430b7103d32b9379760811b6064820152608490fd5b1561402957565b60405162461bcd60e51b815260206004820152601a60248201527f73657474696e6720636f6d7074726f6c6c6572206661696c65640000000000006044820152606490fd5b1561407557565b60405162461bcd60e51b815260206004820152602260248201527f73657474696e6720696e7465726573742072617465206d6f64656c206661696c604482015261195960f21b6064820152608490fd5b90601f82116140d2575050565b6001915f9083825260208220906020601f850160051c83019410614111575b601f0160051c01915b8281106141075750505050565b81815583016140fa565b90925082906140f1565b601f8111614127575050565b5f906002825260208220906020601f850160051c83019410614164575b601f0160051c01915b82811061415957505050565b81815560010161414d565b9092508290614144565b9081516001600160401b038111610521576001906141958161419084546104c2565b6140c5565b602080601f83116001146141ce5750819293945f926141c3575b50505f19600383901b1c191690821b179055565b015190505f806141af565b60015f52601f198316959091907fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6925f905b888210614235575050838596971061421d575b505050811b019055565b01515f1960f88460031b161c191690555f8080614213565b808785968294968601518155019501930190614200565b9081516001600160401b038111610521576142718161426c6002546104c2565b61411b565b602080601f83116001146142ab57508192935f926142a0575b50508160011b915f199060031b1c191617600255565b015190505f8061428a565b60025f52601f198316949091907f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace925f905b8782106143145750508360019596106142fc575b505050811b01600255565b01515f1960f88460031b161c191690555f80806142f1565b806001859682949686015181550195019301906142dd565b600554909691949391906001600160a01b031633036143f6576143bc6143cc956143b76143c29361438f6143896143dc9c61437c6143c79a600b5415806143ec575b61437790613f65565b600955565b6115b86009541515613fbd565b15614022565b61439843600b55565b6143a9670de0b6b3a7640000600e55565b6143b242600c55565b600d55565b615765565b1561406e565b61416e565b61424c565b60ff1660ff196003541617600355565b6113f1600160ff195f5416175f55565b50600e541561436e565b60405162461bcd60e51b8152602060048201526024808201527f6f6e6c792061646d696e206d617920696e697469616c697a6520746865206d616044820152631c9ad95d60e21b6064820152608490fd5b6001600160a01b03918216815291811660208301529091166040820152606081019190915260800190565b90919261447d613460565b50600754614493906001600160a01b0316610915565b602060405180926317b9b84b60e31b8252815f816144b7888c8c3060048601614447565b03925af19081156109a3575f916145f1575b50806145d757506001600160a01b0384811694848216949192918587146145c5575f80516020615bb88339815191529461455b948116870361459d575f195b614512858261338c565b9361454161452387610f8d876137bb565b91614531886126a8836137bb565b9261453b876137bb565b556137bb565b5519614562575b5050604051918252509081906020820190565b0390a35f90565b61457f6145949260018060a01b03165f52601360205260405f2090565b9060018060a01b03165f5260205260405f2090565b555f8080614548565b6001600160a01b0382165f9081526013602052604090206145bf90829061457f565b54614508565b604051638cd22d1960e01b8152600490fd5b60405163089d427760e11b81526004810191909152602490fd5b614609915060203d81116109cf576109c18183610541565b5f6144c9565b90670de0b6b3a76400009182810292818404149015171561157957565b9060015f9215171561157957565b8181029291811591840414171561157957565b8115614657570490565b634e487b7160e01b5f52601260045260245ffd5b6011548061467a575060095490565b6004906146856133bc565b60165460209061469d906001600160a01b0316610915565b6040516314a6bf0f60e01b815294859182905afa9081156109a3576146db6146e9926146e492613411965f926146ee575b50600f54610f0b91613399565b6010549061338c565b61460f565b61464d565b610f0b91925061470b9060203d81116109cf576109c18183610541565b91906146ce565b6001600160a01b03165f908152601460205260409020805490811561474c57600161474361341193600e549061463a565b9101549061464d565b50505f90565b5f549061476160ff8316613ad3565b60ff199182165f55614771613460565b50600161477c61466b565b925f5416175f55565b1561478c57565b60405162461bcd60e51b81526020600482015260186024820152771513d2d15397d514905394d1915497d25397d1905253115160421b6044820152606490fd5b6015546040516370a0823160e01b80825230600483015260209491936001600160a01b03909316929091908585602481875afa9485156109a3575f956148db575b50833b1561046e576040516323b872dd60e01b8152915f91839182916148399190309060048501613b47565b038183875af180156109a3576148c8575b503d8481156148bb575060201461485f575f80fd5b8390815f803e61486f5f51614785565b60405190815230600482015291829060249082905afa9081156109a357613411935f9261489e575b505061338c565b6148b49250803d106109cf576109c18183610541565b5f80614897565b91905061486f5f19614785565b8061099b6148d59261050e565b5f61484a565b6148f3919550863d88116109cf576109c18183610541565b935f61480d565b1561490157565b60405162461bcd60e51b815260206004820152603460248201527f6f6e65206f662072656465656d546f6b656e73496e206f722072656465656d416044820152736d6f756e74496e206d757374206265207a65726f60601b6064820152608490fd5b6007549092919061497c906001600160a01b0316610915565b60206040518092631200453160e11b8252815f816149a089898c3060048601614447565b03925af19081156109a3575f91614b12575b5080614af85750600b544303614ae6576149cb81614712565b925f198303614ae05783925b92306001600160a01b03831603614aa5575b50614a9f614a19847f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1959661338c565b614a2586600f5461338c565b9080614a418660018060a01b03165f52601460205260405f2090565b55600e546001600160a01b0386165f90815260146020526040902060010155614a6982600f55565b866040519586958691959493909260809360a084019760018060a01b038092168552166020840152604083015260608201520152565b0390a190565b7f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a19350614a19614ad8614a9f92846147cc565b9450506149e9565b826149d7565b60405163c9021e2f60e01b8152600490fd5b604051638c81362d60e01b81526004810191909152602490fd5b614b2a915060203d81116109cf576109c18183610541565b5f6149b2565b9081606091031261046e578051916040602083015192015190565b15614b5257565b60405162461bcd60e51b815260206004820152603360248201527f4c49515549444154455f434f4d5054524f4c4c45525f43414c43554c4154455f604482015272105353d5539517d4d152569157d19052531151606a1b6064820152608490fd5b60609060208152601860208201527709892a2aa928882a88abea68a92b48abea89e9ebe9aaa86960431b60408201520190565b15614bed57565b60405162461bcd60e51b81526020600482015260146024820152731d1bdad95b881cd95a5e9d5c994819985a5b195960621b6044820152606490fd5b6001600160a01b0391821681529181166020830152604082019290925291166060820152608081019190915260a00190565b600754614c70906001600160a01b0316610915565b60408051636e2cf5af60e01b8152956001600160a01b0390811692600492919060608980614ca38b8b8a308b8601614447565b03815f80955af19788156109a35781829a839a614fdf575b5080614fc35750600b544303614fb3578351636c540baf60e01b815260209390848188818b5afa9081156109a3578491614f96575b504303614f865780881690891614614f76578015614f66575f198114614f5657614d1b90888861380c565b600754909490614d33906001600160a01b0316610915565b98848051809b63c488847b60e01b82528180614d538b8d308a8501613b47565b03915afa80156109a357839a8491614f33575b509081614d758b939c15614b4b565b86516370a0823160e01b8082526001600160a01b0390941685820190815287908290819060200103818d5afa9081156109a3578691614f16575b5010614e8c575b5050308603614e0057505050918591614a9f93614df67f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb529888883061536e565b5195869586614c29565b9082878593614e268c8c9d99979a98519d8e94859463b2a02ff160e01b86528501613b47565b038184885af180156109a3577f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb5299614a9f97614e6a9392614e6f575b505015614be6565b614df6565b614e859250803d106109cf576109c18183610541565b5f80614e62565b909199508a159081614f0d575b5015614ef65783519081526001600160a01b0388168982019081528390829081906020010381895afa9081156109a3578291614ed9575b50975f80614db6565b614ef09150833d85116109cf576109c18183610541565b5f614ed0565b835162461bcd60e51b815280611bb8818c01614bb3565b9050155f614e99565b614f2d9150873d89116109cf576109c18183610541565b5f614daf565b8a929b50614f4e9150863d88116111b15761119e8183610541565b909a91614d66565b50505051635982c5bb60e11b8152fd5b5050505163d29da7ef60e01b8152fd5b50505051631bd1a62160e21b8152fd5b8451631046f38d60e31b81528690fd5b614fad9150853d87116109cf576109c18183610541565b5f614cf0565b505050516380965b1b60e01b8152fd5b8451630a14d17960e11b81528087019182529081906020010390fd5b9199505061500591995060603d8111615011575b614ffd8183610541565b810190614b30565b9991989099985f614cbb565b503d614ff3565b6001600160a01b0391821681529181166020830152918216604082015291166060820152608081019190915260a00190565b1561505157565b60405162461bcd60e51b815280611bb860048201614bb3565b600754929491939092615085906001600160a01b0316610915565b6040516271dd5d60e41b81526001600160a01b039384169390602081806150b38b8b8b8b3060048701615018565b03815f80975af19081156109a357839161534f575b50806153355750600b54430361532357604051636c540baf60e01b8152602081600481885afa9081156109a3578391615304575b5043036152f257808516908616146152e05785156152ce57604061512461515897878761380c565b60075490939061513c906001600160a01b0316610915565b825180809a8194638a0c3c8d60e01b83528b8a60048501613b47565b03915afa9586156109a357819082976152aa575b506151779015614b4b565b6040516370a0823160e01b81526001600160a01b0386166004820152602081602481875afa9081156109a3576151b8918891849161528b575b50101561504a565b3083036151ff575093613c9a917f1f9e0565f61f991f4274da0b8ede8574d3d06406907d1ae81c972b5c9adcbf7e956151f383878730615520565b60405195869586614c29565b9490916040519263395259dd60e11b845260208480615223868a8a60048501613b47565b03818a865af19687156109a3577f1f9e0565f61f991f4274da0b8ede8574d3d06406907d1ae81c972b5c9adcbf7e97613c9a95615267929161526c575b5015614be6565b6151f3565b615285915060203d6020116109cf576109c18183610541565b5f615260565b6152a4915060203d6020116109cf576109c18183610541565b5f6151b0565b61517797506152c8915060403d6040116111b15761119e8183610541565b9661516c565b60405163d29da7ef60e01b8152600490fd5b604051631bd1a62160e21b8152600490fd5b604051631046f38d60e31b8152600490fd5b61531d915060203d6020116109cf576109c18183610541565b5f6150fc565b6040516380965b1b60e01b8152600490fd5b604051630a14d17960e11b81526004810191909152602490fd5b615368915060203d6020116109cf576109c18183610541565b5f6150c8565b928060208461538761091560075460018060a01b031690565b855f6040996153ac8b519788968795869463d02f735160e01b86523060048701615018565b03925af19081156109a3575f91615502575b50806154e957506001600160a01b038381169290811691908383146154d85793836154bf613c9a94615469967fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc59995615470995f61541a6113e4565b52670de0b6b3a764000061542d8361462c565b049661548e61548861543f8a8661338c565b9461545f612b888c61544f61466b565b6154576113e4565b908152615833565b9d8e601054613399565b9e8f601055565b61547f61198a8c60115461338c565b610f8d846137bb565b916137bb565b5561549f615488836126a8846137bb565b558651805f80516020615bb8833981519152958693829190602083019252565b0390a383519283523092602090a3519283923084613979565b8551633a94626760e11b8152600490fd5b84516363e00e3360e11b81526004810191909152602490fd5b61551a915060203d81116109cf576109c18183610541565b5f6153be565b92602083829495845f61553d61091560075460018060a01b031690565b9261555f6040519788968795869463d02f735160e01b86523060048701615018565b03925af19081156109a3575f91615605575b50806155eb57506001600160a01b0381811693908116928385146155d9575f80516020615bb883398151915292826155b261548884610f8d6155d4976137bb565b556155c3615488836126a8846137bb565b556040519081529081906020820190565b0390a3565b604051633a94626760e11b8152600490fd5b6040516363e00e3360e11b81526004810191909152602490fd5b61561d915060203d81116109cf576109c18183610541565b5f615571565b6001600160a01b0391821681529116602082015260400190565b1561564457565b60405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c7365000000006044820152606490fd5b60055461569e906001600160a01b0316610915565b3303615753576007546001600160a01b0316604051623f1ee960e11b815291906020836004816001600160a01b0386165afa9283156109a3577f7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d9361570a915f91615735575b5061563d565b600780546001600160a01b0319166001600160a01b0384161790555b61364160405192839283615623565b61574d915060203d8111611179576111698183610541565b5f615704565b60405163d219dc1f60e01b8152600490fd5b60055461577a906001600160a01b0316610915565b330361582157600b54430361580f576008546001600160a01b03166040516310c8fc9560e11b815291906020836004816001600160a01b0386165afa9283156109a3577fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926936157ef915f91615735575061563d565b600880546001600160a01b0319166001600160a01b038416179055615726565b604051630be2a5cb60e11b8152600490fd5b60405163407fded560e01b8152600490fd5b9061584c915f60405161584581610526565b525161463a565b6040519061585982610526565b815290565b5115614657575f90565b670de0b6b3a7640000908181029181830414901517156115795761341191519061464d565b6158c26113f193926158b460405194859263a9059cbb60e01b602085015260248401613314565b03601f198101845283610541565b6158ef565b906158c2906158b46113f1956040519586936323b872dd60e01b602086015260248501613b47565b5f806159379260018060a01b03169360208151910182865af13d15615992573d90615919826113f3565b916159276040519384610541565b82523d5f602084013e5b8361599a565b8051908115159182615970575b505061594d5750565b604051635274afe760e01b81526001600160a01b03919091166004820152602490fd5b61598b925090602080615987938301019101613763565b1590565b5f80615944565b606090615931565b906159c157508051156159af57805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806159f4575b6159d2575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156159ca565b634e487b7160e01b5f52603260045260245ffd5b601754811015615a295760175f5260205f2001905f90565b6159fd565b5f81815260186020526040812054615a9b5760175490600160401b821015610521576001820180601755821015615a29577fc624b66cc0138b8fabc209247f72d758e1cf3343756d543badbf24212bed8c1590910182905560175491815260186020526040902055600190565b905090565b6017548015615aca575f19818101919081831015615a29575f916017835260208320010155601755565b634e487b7160e01b5f52603160045260245ffd5b5f81815260186020526040902054801561474c575f199181830191808311611579576017549384019384116115795783835f95615b349503615b3a575b505050615b26615aa0565b5f52601860205260405f2090565b55600190565b615b26615b6291615b5a615b50615b7a95615a11565b90549060031b1c90565b928391615a11565b90919082549060031b91821b915f19901b1916179055565b555f8080615b1b565b601754811015615a295760175f527fc624b66cc0138b8fabc209247f72d758e1cf3343756d543badbf24212bed8c1501549056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122073863b51884c983baa49ccbe8b41f9c5b038c6b7f31de68bd6f18930afb3a4b464736f6c63430008140033000000000000000000000000e5da20f15420ad15de0fa650600afc998bbe3955000000000000000000000000d129538727c0a40197f01ff04e4eafe2c28a10b700000000000000000000000087108936931548f3fe42c148c5ff9dab40e63a38000000000000000000000000000000000000000000a56fa5b99019a5c8000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047786ac88f8cdb8d3a5fe956e51f95b4ae9636fa00000000000000000000000071e2fd817e57fe853b46d51b1f713ef03eaaae12000000000000000000000000000000000000000000000000000000000000000b724574682043546f6b656e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000056372457468000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c806302bd443f1461045f57806306fdde031461045a578063095ea7b3146104555780630e75270214610450578063173b99041461044b578063175188e81461044657806317bfdfbc1461044157806318160ddd1461043c578063182df0f5146104375780631be1956014610432578063223e54791461042d57806323b872dd146104285780632608f81814610423578063267822471461041e5780632e8ebaae14610419578063313ce567146104145780633a76de711461040f5780633af9e6691461040a5780633b1d21a2146104055780633c7a3877146104005780633d5d1d99146103fb5780633e941010146103f65780634576b5db146103f157806347bd3718146103ec5780635fe3b567146103e7578063601a0bf1146103e25780636752e702146103d85780636817031b146103dd57806369ab3250146103d85780636c540baf146103d35780636f307dc3146103ce57806370a08231146103c957806372a4b3ba146103c457806373acee98146103bf578063852a12e3146103ba57806385d89d93146103b5578063889bcdc6146103b05780638ce10c09146103ab5780638f840ddd146103a657806395d89b41146103a157806395dd91931461039c57806396eb171e146103975780639af6702e14610392578063a0712d681461038d578063a6afed9514610388578063a9059cbb14610383578063aa5af0fd1461037e578063ae9d70b014610379578063b2a02ff114610374578063b71d1a0c1461036f578063bd6d894d1461036a578063c37f68e214610365578063c5ebeaec14610360578063cfa992011461035b578063db006a7514610356578063dd62ed3e14610351578063e4e62c581461034c578063e9c714f214610347578063ee27a2f214610342578063f2b3abbd1461033d578063f331b0ec14610338578063f3fdb15a14610333578063f5e3c4621461032e578063f851a44014610329578063f8f9da2814610324578063f9319ff11461031f578063fbfa77cf1461031a578063fca7820b146103155763fe9c44ae14610310575f80fd5b6131a6565b6130c5565b61309d565b61305e565b612f25565b612efd565b612e04565b612ddc565b612dbf565b612d8f565b612d72565b612c62565b612c25565b612bc6565b612b15565b612af8565b612aaf565b612a4d565b612a33565b612997565b612963565b61282f565b612812565b6127cc565b6127b2565b6125aa565b61251a565b611efc565b611ed5565b611e32565b611e15565b611d5f565b611c78565b611bd9565b6118ba565b611873565b61182f565b6117f4565b6117cc565b6117af565b611720565b61173a565b611602565b6115da565b6115bd565b611596565b6114d5565b611454565b610cc5565b610cab565b610c42565b610c20565b610c00565b610bb8565b610b90565b610b44565b610afe565b6109d6565b6108c5565b6108a3565b610886565b61082e565b610772565b610755565b610705565b61067a565b6105a9565b610472565b8015150361046e57565b5f80fd5b3461046e57602036600319011261046e5760043561048f81610464565b6104a460018060a01b0360055416331461332f565b60ff80196019541691151516176019555f80f35b5f91031261046e57565b90600182811c921680156104f0575b60208310146104dc57565b634e487b7160e01b5f52602260045260245ffd5b91607f16916104d1565b634e487b7160e01b5f52604160045260245ffd5b6001600160401b03811161052157604052565b6104fa565b602081019081106001600160401b0382111761052157604052565b90601f801991011681019081106001600160401b0382111761052157604052565b602080825282518183018190529093925f5b82811061059557505060409293505f838284010152601f8019910116010190565b818101860151848201604001528501610574565b3461046e575f80600319360112610666576040518160018054906105cc826104c2565b8085529181811690811561063e5750600114610603575b6105ff846105f381880382610541565b60405191829182610562565b0390f35b80945082526020938483205b82841061062b57505050816105ff936105f392820101936105e3565b805485850187015292850192810161060f565b6105ff96506105f39450602092508593915060ff191682840152151560051b820101936105e3565b80fd5b6001600160a01b0381160361046e57565b3461046e57604036600319011261046e5760043561069781610669565b60243590335f526013602052816106c18260405f209060018060a01b03165f5260205260405f2090565b556040519182526001600160a01b03169033907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3602060405160018152f35b3461046e57602036600319011261046e5760015f5461072660ff8216613ad3565b60ff199081165f55610736613460565b50610744600435333361380c565b505f5416175f5560206040515f8152f35b3461046e575f36600319011261046e576020600a54604051908152f35b3461046e57602036600319011261046e5760043561078f81610669565b6005546001600160a01b0391906107a9908316331461332f565b166107bf815f52601860205260405f2054151590565b156107fb576020816107f17f5a77cb189e9fdbd0240874f9dff8b513450c6d543ff335a7e748ccf3623be5ee93615ade565b50604051908152a1005b60405162461bcd60e51b815260206004820152600b60248201526a1b9bdd081a5b881b1a5cdd60aa1b6044820152606490fd5b3461046e57602036600319011261046e57602060043561084d81610669565b60016108775f549261086160ff8516613ad3565b60ff199384165f55610871613460565b50614712565b915f5416175f55604051908152f35b3461046e575f36600319011261046e576020601154604051908152f35b3461046e575f36600319011261046e5760206108bd61466b565b604051908152f35b3461046e57602036600319011261046e576004356108e281610669565b6005546001600160a01b03908116916108fc338414613249565b6015549116919061092990610921906001600160a01b03165b6001600160a01b031690565b8314156132ad565b6040516370a0823160e01b8152306004820152602081602481865afa9081156109a3575f916109a8575b50823b1561046e5761097e925f928360405180968195829463a9059cbb60e01b845260048401613314565b03925af180156109a35761098e57005b8061099b6109a19261050e565b806104b8565b005b6131d0565b6109c9915060203d81116109cf575b6109c18183610541565b8101906131c1565b5f610953565b503d6109b7565b3461046e57602036600319011261046e576004356109f381610669565b6005546001600160a01b0390610a0c908216331461332f565b600a6017541015610a9757610a22908216615a2e565b15610a60576040516001600160a01b039190911681527f427f3dac4018b5a78b7d3e92b04e7cd4693fc9f4d7b1e5711f803598cd9adb9d90602090a1005b60405162461bcd60e51b815260206004820152600f60248201526e185b1c9958591e481a5b881b1a5cdd608a1b6044820152606490fd5b60405162461bcd60e51b8152602060048201526013602482015272746f6f206d616e79207374726174656769657360681b6044820152606490fd5b606090600319011261046e57600435610aea81610669565b90602435610af781610669565b9060443590565b3461046e576020610b346001610b1336610ad2565b905f9492945494610b2660ff8716613ad3565b60ff199586165f5533614472565b15915f5416175f55604051908152f35b3461046e57604036600319011261046e576001600435610b6381610669565b6107445f5491610b7560ff8416613ad3565b60ff199283165f55610b85613460565b50602435903361380c565b3461046e575f36600319011261046e576006546040516001600160a01b039091168152602090f35b3461046e57602036600319011261046e576020610bf6600435610bda81610669565b6001600160a01b03165f90815260186020526040902054151590565b6040519015158152f35b3461046e575f36600319011261046e57602060ff60035416604051908152f35b3461046e575f36600319011261046e57602060ff601954166040519015158152f35b3461046e57602036600319011261046e576020670de0b6b3a7640000610ca1600435610c6d81610669565b610c75614752565b9060405191610c8383610526565b82526001600160a01b03165f90815260128552604090205490615833565b5104604051908152f35b3461046e575f36600319011261046e5760206108bd6133bc565b3461046e5760a036600319011261046e5760643560046024358135610ce984610669565b601654610cfe906001600160a01b0316610915565b92604094855163980fa2ad60e01b91828252602092838386818b5afa9283156109a3575f936113c5575b506001600160a01b03978893841630149182611373575b82156112c7575b5050610d5190613722565b610d59613460565b501695805163a6afed9560e01b8152828185815f8c5af180156109a3576112aa575b50610d90610915610915610915608435615b83565b8151636f307dc360e01b8152838186818c5afa9081156109a3575f9161127d575b50601654610dc7906001600160a01b0316610915565b90813b1561046e5783516309a5abaf60e11b81528681018981525f602082018190529193849182908490829060400103925af19182156109a357899261126a575b501696610e178630338b6158c7565b610e218787613399565b83516370a0823160e01b808252308883019081529194929187908690819003602001818f5afa9182156109a3578b888e8b985f9661124b575b50895192838080938763095ea7b360e01b9d8e8452830191610e7b92613314565b03915a905f91f19182156109a3578e8a92610eb69461122e575b508b5f8b5180968195829463140e25ad60e31b845283019190602083019252565b03925af180156109a357611211575b5085519081523088820190815287908290819003602001818f5afa9182156109a357610f1792610f10925f916111f4575b50610f0b610f04838361338c565b1515613778565b61338c565b338c61588d565b16968251936301c48f3b60e31b8552808580610f428b8a830160205f91939293604081019481520152565b03818c5afa9485156109a3575f956111d5575b50610f646044358611156137b4565b610f9985610f93610f7433614712565b610f7e3384613b0c565b335f9081526014602052604090205b5461338c565b146137d4565b610fcb8186610fb561091561091560155460018060a01b031690565b8c5f8b8a51968795869485938d85528401613314565b03925af180156109a3576110129a86928b926111b8575b50875f8a85519e8f95869485936315f6307f60e11b855284016040905f9294936060820195825260208201520152565b03925af19283156109a3575f998a94611180575b5060165489918391611040906001600160a01b0316610915565b906110558851948593849384528c8401613314565b03815f875af180156109a357611152575b505060165461107d906001600160a01b0316610915565b803b1561046e578351630164e84f60e51b81525f968101878152909691879182908490829060200103925af19081156109a3577f242797a9cf3b15d6da47c6fb21848e30c43b12d06aab0880c8c9c8af51e4201c9861110b96899361113f575b50828111611122575b50505080611110575b5051938493846040919493926060820195825260208201520152565b0390a2005b61111b90333061380c565b505f6110ef565b6111379261112f9161338c565b90339061588d565b855f806110e6565b8061099b61114c9261050e565b5f6110dd565b8161117192903d10611179575b6111698183610541565b810190613763565b505f80611066565b503d61115f565b829a50899194506111a690863d88116111b1575b61119e8183610541565b8101906133a6565b9a909a949150611026565b503d611194565b6111ce90853d8711611179576111698183610541565b505f610fe2565b816111ed9296503d87116109cf576109c18183610541565b935f610f55565b61120b9150893d8b116109cf576109c18183610541565b5f610ef6565b61122790883d8a116109cf576109c18183610541565b505f610ec5565b61124490843d8611611179576111698183610541565b505f610e95565b611263919650823d84116109cf576109c18183610541565b945f610e5a565b8061099b6112779261050e565b5f610e08565b61129d9150843d86116112a3575b6112958183610541565b81019061370d565b5f610db1565b503d61128b565b6112c090833d85116109cf576109c18183610541565b505f610d7b565b8a516305a7337560e51b81529450915084848781845afa80156109a35789869181965f91611356575b501630149283611307575b505050610d515f610d46565b9194509150858a518095819382525afa80156109a35787610d519181945f91611339575b50168383161490845f6112fb565b6113509150863d88116112a3576112958183610541565b5f61132b565b61136d9150833d85116112a3576112958183610541565b5f6112f0565b8a516305a7337560e51b81529450915084848781845afa9384156109a357899485915f916113a8575b50168484161491610d3f565b6113bf9150873d89116112a3576112958183610541565b5f61139c565b6113dd919350843d86116112a3576112958183610541565b915f610d28565b604051906113f182610526565b565b6001600160401b03811161052157601f01601f191660200190565b81601f8201121561046e57803590611425826113f3565b926114336040519485610541565b8284526020838301011161046e57815f926020809301838601378301015290565b3461046e5760e036600319011261046e5760043561147181610669565b60243561147d81610669565b6001600160401b0360643581811161046e5761149d90369060040161140e565b9060843590811161046e576114b690369060040161140e565b60a4359160ff8316830361046e576109a19460c435946044359161432c565b3461046e57602036600319011261046e575f546114f460ff8216613ad3565b60ff19165f55611502613460565b50600b54430361157e57611518600435336147cc565b601054818101809111611579577fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc5918160105561155b6040519283923384613979565b0390a161156e600160ff195f5416175f55565b6040515f8152602090f35b613368565b6040516338acf79960e01b81525f6004820152602490fd5b3461046e57602036600319011261046e5760206108bd6004356115b881610669565b615689565b3461046e575f36600319011261046e576020600f54604051908152f35b3461046e575f36600319011261046e576007546040516001600160a01b039091168152602090f35b3461046e57602036600319011261046e576004355f5461162460ff8216613ad3565b60ff19165f55611632613460565b50600554611648906001600160a01b0316610915565b330361170e57600b5443036116fc57806116606133bc565b106116ea57601054908181116116d85761169b817f3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e9361338c565b6116a481601055565b6005546116bb9083906001600160a01b0316613a0e565b60055461155b906001600160a01b03169160405193849384613979565b6040516378d2980560e11b8152600490fd5b604051633345e99960e01b8152600490fd5b604051630dff50cb60e41b8152600490fd5b604051630f7e5e6d60e41b8152600490fd5b3461046e575f36600319011261046e5760206040515f8152f35b3461046e57602036600319011261046e577fd459c7242e23d490831b5676a611c4342d899d28f342d89ae80793e56a930f30602060043561177a81610669565b6005546001600160a01b039190611794908316331461332f565b168060018060a01b03196016541617601655604051908152a1005b3461046e575f36600319011261046e576020600b54604051908152f35b3461046e575f36600319011261046e576015546040516001600160a01b039091168152602090f35b3461046e57602036600319011261046e5760043561181181610669565b60018060a01b03165f526012602052602060405f2054604051908152f35b3461046e57600161186361184236610ad2565b905f939293549361185560ff8616613ad3565b60ff199485165f5533615520565b5f5416175f5560206040515f8152f35b3461046e575f8060031936011261066657805460209161189560ff8316613ad3565b60ff1991821681556118a5613460565b506001600f5492825416179055604051908152f35b3461046e5760208060031936011261046e575f805460048035906118e060ff8416613ad3565b60ff1992831684556118f0613460565b5061190d6118fc61466b565b6119046113e4565b90815283615868565b600754909290611925906001600160a01b0316610915565b95604096818851809263eabe7d9160e01b8252818a816119498b33308d8501613b47565b03925af19081156109a3578791611bbc575b5080611b9c5750600b544303611b8c57816119746133bc565b10611a8e575b5061198f61198a8460115461338c565b601155565b61199c83610f8d336137bb565b6119a5336137bb565b556119b08133613a0e565b8551838152309033905f80516020615bb883398151915290602090a37fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a9298651806119fc86853384613979565b0390a1600754611a14906001600160a01b0316610915565b91823b15611a8a5786516351dff98960e01b8152309181019182523360208301526040820192909252606081019390935292918491849182908490829060800103925af19182156109a357600192611a77575b50825416179055515f8152602090f35b8061099b611a849261050e565b5f611a67565b8580fd5b611a9f611a996133bc565b8361338c565b6016548790611ab6906001600160a01b0316610915565b926001600160a01b038416611b32575b508111611b2257813b15611b1e57875163317afabb60e21b815284810191825291879183919082908490829060200103925af180156109a357611b0b575b505f61197a565b8061099b611b189261050e565b5f611b04565b8680fd5b87516391240a1b60e01b81528490fd5b9050885163cbb575bf60e01b8152818180611b54898201906001602083019252565b0381875afa9182156109a3578992611b6f575b50505f611ac6565b611b859250803d106109cf576109c18183610541565b5f80611b67565b86516397b5cfcd60e01b81528390fd5b875163480f424760e01b81528085019182529081906020010390fd5b0390fd5b611bd39150823d84116109cf576109c18183610541565b5f61195b565b3461046e575f8060031936011261066657604051809160175490818352602080930180926017835284832090835b818110611c645750505084611c1d910385610541565b60405193838594850191818652518092526040850193925b828110611c4457505050500390f35b83516001600160a01b031685528695509381019392810192600101611c35565b825484529286019260019283019201611c07565b3461046e57608036600319011261046e575f600435611c9681610669565b60643590611ca382610669565b602060018060a01b03611cbb81601654163314613f2c565b8454611cc960ff8216613ad3565b60ff19168555611cd7613460565b5060046040518096819363a6afed9560e01b835287165af19283156109a3575f93611d3f575b5082611d2657611d16925060443590602435903361506a565b61156e600160ff195f5416175f55565b604051633eea49b760e11b815260048101849052602490fd5b611d5891935060203d81116109cf576109c18183610541565b915f611cfd565b3461046e57606036600319011261046e57611dd16020602435611d8181610464565b611da4611d8f604435615a11565b905460039190911b1c6001600160a01b031690565b6040516301c48f3b60e31b8152600480359082015291151560248301529092839190829081906044820190565b03915afa80156109a3576105ff915f91611df7575b506040519081529081906020820190565b611e0f915060203d81116109cf576109c18183610541565b5f611de6565b3461046e575f36600319011261046e576020601054604051908152f35b3461046e575f806003193601126106665760405181600254611e53816104c2565b8084529060019081811690811561063e5750600114611e7c576105ff846105f381880382610541565b60028352602094507f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace5b828410611ec257505050816105ff936105f392820101936105e3565b8054858501870152928501928101611ea6565b3461046e57602036600319011261046e5760206108bd600435611ef781610669565b614712565b3461046e57608036600319011261046e576004803590611f1b82610669565b60165460643590602435908190611f3a906001600160a01b0316610915565b60409182519163980fa2ad60e01b9081845260209182858a81855afa9485156109a3575f956124fb575b506001600160a01b03948516301490816124ad575b8115612404575b50611f8b9150613722565b611f93613460565b50835163a6afed9560e01b8152888416989082818a815f8e5af180156109a3576123e7575b5087611fcc6109156109156109158b615b83565b93838b885193848092636f307dc360e01b82525afa9182156109a3575f926123c8575b50611ff933614712565b8091116123c0575b50601654612017906001600160a01b0316610915565b803b1561046e5786516309a5abaf60e11b8152808b018981526001602082015290915f9183919082908490829060400103925af19889156109a3578887946120749361208c9c6123ad575b5061206e82333061380c565b50613e0e565b98906120846044358b11156137b4565b30338d6158c7565b85516370a0823160e01b808252308b830190815292909316959294918490829081906020010381895afa9081156109a3575f91612390575b50865163852a12e360e01b8152808b018a8152859082908e9082905f90829060200103925af180156109a357612373575b508651948552308a86019081528490869081906020010381895afa80156109a35761212e612135928b928d985f91612356575b5061338c565b1015613eec565b168451908282806121548b8563095ea7b360e01b998a85528401613314565b03815f895af19081156109a35761219e9887938993612339575b505f8b85519b8c95869485936315f6307f60e11b8552840160409060019294936060820195825260208201520152565b03925af19182156109a3575f968793612311575b50601554869183916121ce90610915906001600160a01b031681565b6016548b905f906121e7906001600160a01b0316610915565b936121fd8b519788968795869485528401613314565b03925af180156109a3576122f3575b5050601654612223906001600160a01b0316610915565b803b1561046e578351630164e84f60e51b81526001978101978852965f91889182908490829060200103925af19485156109a3577f5f6909a82162596722a5e14496b5ca56b573b7c08a42b4182bf7622b5a65e3189661110b966122e0575b508481116122b4575b50806122a2575b5050519081529081906020820190565b6122ad91339061588d565b5f80612292565b6015546122da919061112f9087906122d4906001600160a01b0316610915565b9261338c565b5f61228b565b8061099b6122ed9261050e565b5f612282565b8161230992903d10611179576111698183610541565b505f8061220c565b8297508691935061232e90863d88116111b15761119e8183610541565b9790979391506121b2565b61234f90863d8811611179576111698183610541565b505f61216e565b61236d9150883d8a116109cf576109c18183610541565b5f612128565b61238990853d87116109cf576109c18183610541565b505f6120f5565b6123a79150843d86116109cf576109c18183610541565b5f6120c4565b8061099b6123ba9261050e565b5f612062565b96505f612001565b6123e0919250843d86116112a3576112958183610541565b905f611fef565b6123fd90833d85116109cf576109c18183610541565b505f611fb8565b86516305a7337560e51b815290915083818b81865afa9081156109a357849187915f91612490575b501630149182612444575b5050611f8b91505f611f80565b9091508987518094819382525afa9081156109a357611f8b9185915f91612473575b5016848a1614825f612437565b61248a9150843d86116112a3576112958183610541565b5f612466565b6124a79150833d85116112a3576112958183610541565b5f61242c565b86516305a7337560e51b815290915083818b81865afa80156109a35786915f916124de575b5016858b161490611f79565b6124f59150853d87116112a3576112958183610541565b5f6124d2565b612513919550833d85116112a3576112958183610541565b935f611f64565b3461046e5761010036600319011261046e5760043561253881610669565b60243561254481610669565b6044359161255183610669565b6001600160401b0360843581811161046e5761257190369060040161140e565b9060a43590811161046e5761258a90369060040161140e565b9060c4359260ff8416840361046e576109a19560e43595606435926131db565b3461046e57602036600319011261046e576004355f546125cc60ff8216613ad3565b60ff19165f556125da613460565b506007546125f0906001600160a01b0316610915565b90604091602083518092634ef4c3e160e01b8252815f8161261688333060048501613b47565b03925af19081156109a3575f91612794575b508061277b5750600b54430361276a5761265f61265861264661466b565b9261264f6113e4565b938452336147cc565b9182615868565b90601154156126ff575b817f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f9161269b61198a83601154613399565b6126ae826126a8336137bb565b54613399565b6126b7336137bb565b556126c785519283923384613979565b0390a18151908152339030905f80516020615bb883398151915290602090a36126f6600160ff195f5416175f55565b515f8152602090f35b906127627f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f916127306103e8601155565b5f805260126020526103e87f7e7fa33969761a458e04f477e039a608702b4f924981d6653935a8319a08ad7b5561337c565b919050612669565b81516338d8859760e01b8152600490fd5b82516349abd4fd60e01b81526004810191909152602490fd5b6127ac915060203d81116109cf576109c18183610541565b5f612628565b3461046e575f36600319011261046e5760206108bd613460565b3461046e57604036600319011261046e5760206004356127eb81610669565b6001610b345f54926127ff60ff8516613ad3565b60ff199384165f55602435903380614472565b3461046e575f36600319011261046e576020600e54604051908152f35b3461046e575f80600319360112610666576016548190612857906001600160a01b0316610915565b6001600160a01b03811661290f575b506020612875600c544261338c565b60085490929061289f90612891906001600160a01b0316610915565b9161289a6133bc565b613399565b600f54601054600a54600d546040516333d7bb6560e01b81526004810195909552602485019390935260448401919091526064830152608482019490945260a4810193909352829060c49082905afa9081156109a3576105ff9291611df757506040519081529081906020820190565b60405163cbb575bf60e01b81525f60048201529150602090829060249082905afa9081156109a3578291612945575b505f612866565b61295d915060203d81116109cf576109c18183610541565b5f61293e565b3461046e57600161186361297636610ad2565b905f939293549361298960ff8616613ad3565b60ff199485165f553361536e565b3461046e57602036600319011261046e576004356129b481610669565b6005546001600160a01b039081163303612a2157600680546001600160a01b03198116838516179091556040517fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a99390928392612a1392911683615623565b0390a16040515f8152602090f35b604051635cb56c2b60e01b8152600490fd5b3461046e575f36600319011261046e5760206108bd614752565b3461046e57602036600319011261046e576080600435612a6c81610669565b6001600160a01b0381165f9081526012602052604090205490612a8e90614712565b612a9661466b565b90604051925f8452602084015260408301526060820152f35b3461046e57602036600319011261046e5760016004356118635f5491612ad760ff8416613ad3565b60ff199283165f55612ae7613460565b50612af28133613b69565b33613a0e565b3461046e575f36600319011261046e576020600c54604051908152f35b3461046e5760208060031936011261046e575f80546004803590612b3b60ff8416613ad3565b60ff199283168455612b4b613460565b5081159182159283612bbe575b612b61906148fa565b612b6961466b565b92612b726113e4565b93845215612bac57612b8881612b979294615833565b670de0b6b3a764000090510490565b600754611925906001600160a01b0316610915565b5090612bb79061585e565b9083612b97565b506001612b58565b3461046e57604036600319011261046e576020612c1c600435612be881610669565b60243590612bf582610669565b60018060a01b03165f526013835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b3461046e57602036600319011261046e576005546001600160a01b03163303612c5057600480359055005b6040516346e71bad60e11b8152600490fd5b3461046e575f36600319011261046e576006546001600160a01b031680338114801590612d6a575b612d58576005547fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9927ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc91612d0d90612ceb906001600160a01b0316610915565b600580546001600160a01b0319166001600160a01b0390941693909317909255565b600680546001600160a01b03191690556005546001600160a01b031690612d3960405192839283615623565b0390a16006546001600160a01b031690612a1360405192839283615623565b604051631ba24f2960e21b8152600490fd5b503315612c8a565b3461046e575f36600319011261046e576020600454604051908152f35b3461046e57602036600319011261046e5760206108bd600435612db181610669565b612db9613460565b50615765565b3461046e575f36600319011261046e576020600d54604051908152f35b3461046e575f36600319011261046e576008546040516001600160a01b039091168152602090f35b3461046e57606036600319011261046e57600435612e2181610669565b60443590612e2e82610669565b5f602060018060a01b03612e4781601654163314613f2c565b8254612e5560ff8216613ad3565b60ff19168355612e63613460565b5060046040518094819363a6afed9560e01b835288165af19081156109a3575f91612edf575b5080612ec7576105ff612ea0846024358533614c5b565b612eb0600160ff195f5416175f55565b604051918291829190602060408401935f81520152565b60249060405190633eea49b760e11b82526004820152fd5b612ef7915060203d81116109cf576109c18183610541565b5f612e89565b3461046e575f36600319011261046e576005546040516001600160a01b039091168152602090f35b3461046e575f80600319360112610666576016548190612f4d906001600160a01b0316610915565b6001600160a01b03811661300a575b506040612f6b600c544261338c565b600854909290612f8790612891906001600160a01b0316610915565b600f54601054600d54855163108e627960e21b815260048101949094526024840192909252604483015260648201949094526084810193909352829060a49082905afa9081156109a3576105ff9291612feb57506040519081529081906020820190565b613003915060403d81116111b15761119e8183610541565b505f611de6565b60405163cbb575bf60e01b81525f60048201529150602090829060249082905afa9081156109a3578291613040575b505f612f5c565b613058915060203d81116109cf576109c18183610541565b5f613039565b3461046e57606036600319011261046e5761308b60043561307e81610669565b6044359060243590613e0e565b60408051928352602083019190915290f35b3461046e575f36600319011261046e576016546040516001600160a01b039091168152602090f35b3461046e57602036600319011261046e576004355f546130e760ff8216613ad3565b60ff19165f556130f5613460565b5060055461310b906001600160a01b0316610915565b330361319457600b54430361318257670de0b6b3a76400008111613170577faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f82146090600a5461315782600a55565b604080519182526020820192909252908190810161155b565b60405163717220f360e11b8152600490fd5b604051637dfca6b760e11b8152600490fd5b604051631205b57b60e11b8152600490fd5b3461046e575f36600319011261046e57602060405160018152f35b9081602091031261046e575190565b6040513d5f823e3d90fd5b94909360049793602097936131ef9661432c565b601580546001600160a01b0319166001600160a01b039290921691821790556040516318160ddd60e01b815292839182905afa80156109a35761322f5750565b6132469060203d81116109cf576109c18183610541565b50565b1561325057565b60405162461bcd60e51b815260206004820152602f60248201527f4345726332303a3a7377656570546f6b656e3a206f6e6c792061646d696e206360448201526e616e20737765657020746f6b656e7360881b6064820152608490fd5b156132b457565b60405162461bcd60e51b815260206004820152603260248201527f4345726332303a3a7377656570546f6b656e3a2063616e206e6f74207377656560448201527138103ab73232b9363cb4b733903a37b5b2b760711b6064820152608490fd5b6001600160a01b039091168152602081019190915260400190565b1561333657565b60405162461bcd60e51b815260206004820152600a60248201526937b7363c9030b236b4b760b11b6044820152606490fd5b634e487b7160e01b5f52601160045260245ffd5b6103e71981019190821161157957565b9190820391821161157957565b9190820180921161157957565b919082604091031261046e576020825192015190565b6015546040516370a0823160e01b815230600482015290602090829060249082906001600160a01b03165afa9081156109a3575f916133f9575090565b613411915060203d81116109cf576109c18183610541565b90565b1561341b57565b60405162461bcd60e51b815260206004820152601c60248201527f626f72726f772072617465206973206162737572646c792068696768000000006044820152606490fd5b600b54438114613708576016545f9190613482906001600160a01b0316610915565b6001600160a01b0381166136ad575b506134a16135179261289a6133bc565b600f54601054600e546008549294926134c2906001600160a01b0316610915565b6134ce600c544261338c565b600d546040805163108e627960e21b815260048101899052602481018a9052604481018790526064810193909352608483019190915297909188918391908290819060a4820190565b03915afa9384156109a3575f91829561368a575b5090829160045482111561353e90613414565b613548904361338c565b6135506113e4565b91825261355c91615833565b6135668782615833565b51670de0b6b3a764000090049661357d9088613399565b93600a548861358a6113e4565b91825261359691615833565b51670de0b6b3a76400009004906135ac91613399565b926135b691615833565b51670de0b6b3a76400009004906135cc91613399565b43600b559042600c556135de82600e55565b6135e783600f55565b601055600d54958387141596613641947f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc0498613648575b505051948594859094939260609260808301968352602083015260408201520152565b0390a15f90565b825191825260208201819052613683917ffe3b4c3f6e2efdc3dd612c3eb2817308b7568ad72d3f9c9200a08a069c27dff890604090a1600d55565b5f8061361e565b6136a5919550839250883d8a116111b15761119e8183610541565b94909161352b565b60405163cbb575bf60e01b81525f60048201529290602090849060249082905afa80156109a357613517936134a192916136ea575b509250613491565b613702915060203d81116109cf576109c18183610541565b5f6136e2565b505f90565b9081602091031261046e575161341181610669565b1561372957565b60405162461bcd60e51b81526020600482015260126024820152711a5b9d985b1a590818dbdb1b185d195c985b60721b6044820152606490fd5b9081602091031261046e575161341181610464565b1561377f57565b60405162461bcd60e51b815260206004820152600d60248201526c1b9bc818dbdb1b185d195c985b609a1b6044820152606490fd5b1561046e57565b6001600160a01b03165f90815260126020526040902090565b156137db57565b60405162461bcd60e51b8152602060048201526009602482015268626f72726f77206b6f60b81b6044820152606490fd5b906138179291614963565b60165461382c906001600160a01b0316610915565b6040516314a6bf0f60e01b8152906020908183600481845afa9283156109a3575f9361395a575b508261387c575b50505061386960195460ff1690565b6138705790565b613878613997565b5090565b82841115613952575b81836138c0926138a261091561091560155460018060a01b031690565b905f60405180968195829463095ea7b360e01b845260048401613314565b03925af180156109a357613934575b50506016546138e6906001600160a01b0316610915565b803b1561046e57604051631b8fec7360e11b815260048101929092525f908290602490829084905af180156109a357613921575b808061385a565b8061099b61392e9261050e565b5f61391a565b8161394a92903d10611179576111698183610541565b505f806138cf565b839250613885565b613972919350823d84116109cf576109c18183610541565b915f613853565b604091949392606082019560018060a01b0316825260208201520152565b61399f6133bc565b6010548091818110613a06575b5080820391821161157957817f3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e9260105561364160018060a01b036139f5848260165416613a0e565b601654169160405193849384613979565b90505f6139ac565b919060018060a01b036015541690813b1561046e57613a48936040519283809263a9059cbb60e01b8252815f988996879360048401613314565b03925af180156109a357613ac4575b503d8015613aba57602014613a6a575080fd5b90602081803e515b15613a7957565b60405162461bcd60e51b81526020600482015260196024820152781513d2d15397d514905394d1915497d3d55517d19052531151603a1b6044820152606490fd5b5090505f19613a72565b613acd9061050e565b5f613a57565b15613ada57565b60405162461bcd60e51b815260206004820152600a6024820152691c994b595b9d195c995960b21b6044820152606490fd5b90613b3f6001925f5492613b2260ff8516613ad3565b60ff199384165f55613b32613460565b50848060a01b0316613b69565b5f5416175f55565b6001600160a01b03918216815291166020820152604081019190915260600190565b60075490929190613b82906001600160a01b0316610915565b6040805163368f515360e21b81526004959290602090818180613ba98989308e8501613b47565b03815f80975af19081156109a3578391613df1575b5080613dd55750600b544303613dc557613bd66133bc565b858110613c9f575b509495507f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab809450613c9a919050613c218461289a6001600160a01b038616614712565b613c2d85600f54613399565b9181613c498660018060a01b03165f52601460205260405f2090565b55600e546001600160a01b0386165f90815260146020526040902060010155613c7183600f55565b519485948590949392606092608083019660018060a01b03168352602083015260408201520152565b0390a1565b601654613cb4906001600160a01b0316610915565b906001600160a01b03821615613db557613cce908761338c565b91845163cbb575bf60e01b8152818180613cef8d8201906001602083019252565b0381865afa9182156109a3578592613d98575b50508211613d8857803b15613d8457835163317afabb60e21b8152978801918252959695869182908490829060200103925af19081156109a3577f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab8094613c9a92613d71575b509085945f613bde565b8061099b613d7e9261050e565b5f613d67565b8280fd5b83516348c2588160e01b81528890fd5b613dae9250803d106109cf576109c18183610541565b5f80613d02565b84516348c2588160e01b81528990fd5b8251630e8d8c6160e21b81528790fd5b835163918db40f60e01b81528089019182529081906020010390fd5b613e089150823d84116109cf576109c18183610541565b5f613bbe565b613e28610915610915610915610915613e56969897615b83565b6040516301c48f3b60e31b815260048101959095526001602486015260209283918691829081906044820190565b03915afa9384156109a3575f94613ec8575b5060405163182df0f560e01b8152908290829060049082906001600160a01b03165afa9081156109a357613ea7925f92613eab575b50506119046113e4565b9190565b613ec19250803d106109cf576109c18183610541565b5f80613e9d565b6004919450613ee48391823d84116109cf576109c18183610541565b949150613e68565b15613ef357565b60405162461bcd60e51b81526020600482015260116024820152706e6f7420656e6f7567682072656465656d60781b6044820152606490fd5b15613f3357565b60405162461bcd60e51b815260206004820152600a6024820152697661756c74206f6e6c7960b01b6044820152606490fd5b15613f6c57565b60405162461bcd60e51b815260206004820152602360248201527f6d61726b6574206d6179206f6e6c7920626520696e697469616c697a6564206f6044820152626e636560e81b6064820152608490fd5b15613fc457565b60405162461bcd60e51b815260206004820152603060248201527f696e697469616c2065786368616e67652072617465206d75737420626520677260448201526f32b0ba32b9103a3430b7103d32b9379760811b6064820152608490fd5b1561402957565b60405162461bcd60e51b815260206004820152601a60248201527f73657474696e6720636f6d7074726f6c6c6572206661696c65640000000000006044820152606490fd5b1561407557565b60405162461bcd60e51b815260206004820152602260248201527f73657474696e6720696e7465726573742072617465206d6f64656c206661696c604482015261195960f21b6064820152608490fd5b90601f82116140d2575050565b6001915f9083825260208220906020601f850160051c83019410614111575b601f0160051c01915b8281106141075750505050565b81815583016140fa565b90925082906140f1565b601f8111614127575050565b5f906002825260208220906020601f850160051c83019410614164575b601f0160051c01915b82811061415957505050565b81815560010161414d565b9092508290614144565b9081516001600160401b038111610521576001906141958161419084546104c2565b6140c5565b602080601f83116001146141ce5750819293945f926141c3575b50505f19600383901b1c191690821b179055565b015190505f806141af565b60015f52601f198316959091907fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6925f905b888210614235575050838596971061421d575b505050811b019055565b01515f1960f88460031b161c191690555f8080614213565b808785968294968601518155019501930190614200565b9081516001600160401b038111610521576142718161426c6002546104c2565b61411b565b602080601f83116001146142ab57508192935f926142a0575b50508160011b915f199060031b1c191617600255565b015190505f8061428a565b60025f52601f198316949091907f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace925f905b8782106143145750508360019596106142fc575b505050811b01600255565b01515f1960f88460031b161c191690555f80806142f1565b806001859682949686015181550195019301906142dd565b600554909691949391906001600160a01b031633036143f6576143bc6143cc956143b76143c29361438f6143896143dc9c61437c6143c79a600b5415806143ec575b61437790613f65565b600955565b6115b86009541515613fbd565b15614022565b61439843600b55565b6143a9670de0b6b3a7640000600e55565b6143b242600c55565b600d55565b615765565b1561406e565b61416e565b61424c565b60ff1660ff196003541617600355565b6113f1600160ff195f5416175f55565b50600e541561436e565b60405162461bcd60e51b8152602060048201526024808201527f6f6e6c792061646d696e206d617920696e697469616c697a6520746865206d616044820152631c9ad95d60e21b6064820152608490fd5b6001600160a01b03918216815291811660208301529091166040820152606081019190915260800190565b90919261447d613460565b50600754614493906001600160a01b0316610915565b602060405180926317b9b84b60e31b8252815f816144b7888c8c3060048601614447565b03925af19081156109a3575f916145f1575b50806145d757506001600160a01b0384811694848216949192918587146145c5575f80516020615bb88339815191529461455b948116870361459d575f195b614512858261338c565b9361454161452387610f8d876137bb565b91614531886126a8836137bb565b9261453b876137bb565b556137bb565b5519614562575b5050604051918252509081906020820190565b0390a35f90565b61457f6145949260018060a01b03165f52601360205260405f2090565b9060018060a01b03165f5260205260405f2090565b555f8080614548565b6001600160a01b0382165f9081526013602052604090206145bf90829061457f565b54614508565b604051638cd22d1960e01b8152600490fd5b60405163089d427760e11b81526004810191909152602490fd5b614609915060203d81116109cf576109c18183610541565b5f6144c9565b90670de0b6b3a76400009182810292818404149015171561157957565b9060015f9215171561157957565b8181029291811591840414171561157957565b8115614657570490565b634e487b7160e01b5f52601260045260245ffd5b6011548061467a575060095490565b6004906146856133bc565b60165460209061469d906001600160a01b0316610915565b6040516314a6bf0f60e01b815294859182905afa9081156109a3576146db6146e9926146e492613411965f926146ee575b50600f54610f0b91613399565b6010549061338c565b61460f565b61464d565b610f0b91925061470b9060203d81116109cf576109c18183610541565b91906146ce565b6001600160a01b03165f908152601460205260409020805490811561474c57600161474361341193600e549061463a565b9101549061464d565b50505f90565b5f549061476160ff8316613ad3565b60ff199182165f55614771613460565b50600161477c61466b565b925f5416175f55565b1561478c57565b60405162461bcd60e51b81526020600482015260186024820152771513d2d15397d514905394d1915497d25397d1905253115160421b6044820152606490fd5b6015546040516370a0823160e01b80825230600483015260209491936001600160a01b03909316929091908585602481875afa9485156109a3575f956148db575b50833b1561046e576040516323b872dd60e01b8152915f91839182916148399190309060048501613b47565b038183875af180156109a3576148c8575b503d8481156148bb575060201461485f575f80fd5b8390815f803e61486f5f51614785565b60405190815230600482015291829060249082905afa9081156109a357613411935f9261489e575b505061338c565b6148b49250803d106109cf576109c18183610541565b5f80614897565b91905061486f5f19614785565b8061099b6148d59261050e565b5f61484a565b6148f3919550863d88116109cf576109c18183610541565b935f61480d565b1561490157565b60405162461bcd60e51b815260206004820152603460248201527f6f6e65206f662072656465656d546f6b656e73496e206f722072656465656d416044820152736d6f756e74496e206d757374206265207a65726f60601b6064820152608490fd5b6007549092919061497c906001600160a01b0316610915565b60206040518092631200453160e11b8252815f816149a089898c3060048601614447565b03925af19081156109a3575f91614b12575b5080614af85750600b544303614ae6576149cb81614712565b925f198303614ae05783925b92306001600160a01b03831603614aa5575b50614a9f614a19847f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1959661338c565b614a2586600f5461338c565b9080614a418660018060a01b03165f52601460205260405f2090565b55600e546001600160a01b0386165f90815260146020526040902060010155614a6982600f55565b866040519586958691959493909260809360a084019760018060a01b038092168552166020840152604083015260608201520152565b0390a190565b7f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a19350614a19614ad8614a9f92846147cc565b9450506149e9565b826149d7565b60405163c9021e2f60e01b8152600490fd5b604051638c81362d60e01b81526004810191909152602490fd5b614b2a915060203d81116109cf576109c18183610541565b5f6149b2565b9081606091031261046e578051916040602083015192015190565b15614b5257565b60405162461bcd60e51b815260206004820152603360248201527f4c49515549444154455f434f4d5054524f4c4c45525f43414c43554c4154455f604482015272105353d5539517d4d152569157d19052531151606a1b6064820152608490fd5b60609060208152601860208201527709892a2aa928882a88abea68a92b48abea89e9ebe9aaa86960431b60408201520190565b15614bed57565b60405162461bcd60e51b81526020600482015260146024820152731d1bdad95b881cd95a5e9d5c994819985a5b195960621b6044820152606490fd5b6001600160a01b0391821681529181166020830152604082019290925291166060820152608081019190915260a00190565b600754614c70906001600160a01b0316610915565b60408051636e2cf5af60e01b8152956001600160a01b0390811692600492919060608980614ca38b8b8a308b8601614447565b03815f80955af19788156109a35781829a839a614fdf575b5080614fc35750600b544303614fb3578351636c540baf60e01b815260209390848188818b5afa9081156109a3578491614f96575b504303614f865780881690891614614f76578015614f66575f198114614f5657614d1b90888861380c565b600754909490614d33906001600160a01b0316610915565b98848051809b63c488847b60e01b82528180614d538b8d308a8501613b47565b03915afa80156109a357839a8491614f33575b509081614d758b939c15614b4b565b86516370a0823160e01b8082526001600160a01b0390941685820190815287908290819060200103818d5afa9081156109a3578691614f16575b5010614e8c575b5050308603614e0057505050918591614a9f93614df67f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb529888883061536e565b5195869586614c29565b9082878593614e268c8c9d99979a98519d8e94859463b2a02ff160e01b86528501613b47565b038184885af180156109a3577f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb5299614a9f97614e6a9392614e6f575b505015614be6565b614df6565b614e859250803d106109cf576109c18183610541565b5f80614e62565b909199508a159081614f0d575b5015614ef65783519081526001600160a01b0388168982019081528390829081906020010381895afa9081156109a3578291614ed9575b50975f80614db6565b614ef09150833d85116109cf576109c18183610541565b5f614ed0565b835162461bcd60e51b815280611bb8818c01614bb3565b9050155f614e99565b614f2d9150873d89116109cf576109c18183610541565b5f614daf565b8a929b50614f4e9150863d88116111b15761119e8183610541565b909a91614d66565b50505051635982c5bb60e11b8152fd5b5050505163d29da7ef60e01b8152fd5b50505051631bd1a62160e21b8152fd5b8451631046f38d60e31b81528690fd5b614fad9150853d87116109cf576109c18183610541565b5f614cf0565b505050516380965b1b60e01b8152fd5b8451630a14d17960e11b81528087019182529081906020010390fd5b9199505061500591995060603d8111615011575b614ffd8183610541565b810190614b30565b9991989099985f614cbb565b503d614ff3565b6001600160a01b0391821681529181166020830152918216604082015291166060820152608081019190915260a00190565b1561505157565b60405162461bcd60e51b815280611bb860048201614bb3565b600754929491939092615085906001600160a01b0316610915565b6040516271dd5d60e41b81526001600160a01b039384169390602081806150b38b8b8b8b3060048701615018565b03815f80975af19081156109a357839161534f575b50806153355750600b54430361532357604051636c540baf60e01b8152602081600481885afa9081156109a3578391615304575b5043036152f257808516908616146152e05785156152ce57604061512461515897878761380c565b60075490939061513c906001600160a01b0316610915565b825180809a8194638a0c3c8d60e01b83528b8a60048501613b47565b03915afa9586156109a357819082976152aa575b506151779015614b4b565b6040516370a0823160e01b81526001600160a01b0386166004820152602081602481875afa9081156109a3576151b8918891849161528b575b50101561504a565b3083036151ff575093613c9a917f1f9e0565f61f991f4274da0b8ede8574d3d06406907d1ae81c972b5c9adcbf7e956151f383878730615520565b60405195869586614c29565b9490916040519263395259dd60e11b845260208480615223868a8a60048501613b47565b03818a865af19687156109a3577f1f9e0565f61f991f4274da0b8ede8574d3d06406907d1ae81c972b5c9adcbf7e97613c9a95615267929161526c575b5015614be6565b6151f3565b615285915060203d6020116109cf576109c18183610541565b5f615260565b6152a4915060203d6020116109cf576109c18183610541565b5f6151b0565b61517797506152c8915060403d6040116111b15761119e8183610541565b9661516c565b60405163d29da7ef60e01b8152600490fd5b604051631bd1a62160e21b8152600490fd5b604051631046f38d60e31b8152600490fd5b61531d915060203d6020116109cf576109c18183610541565b5f6150fc565b6040516380965b1b60e01b8152600490fd5b604051630a14d17960e11b81526004810191909152602490fd5b615368915060203d6020116109cf576109c18183610541565b5f6150c8565b928060208461538761091560075460018060a01b031690565b855f6040996153ac8b519788968795869463d02f735160e01b86523060048701615018565b03925af19081156109a3575f91615502575b50806154e957506001600160a01b038381169290811691908383146154d85793836154bf613c9a94615469967fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc59995615470995f61541a6113e4565b52670de0b6b3a764000061542d8361462c565b049661548e61548861543f8a8661338c565b9461545f612b888c61544f61466b565b6154576113e4565b908152615833565b9d8e601054613399565b9e8f601055565b61547f61198a8c60115461338c565b610f8d846137bb565b916137bb565b5561549f615488836126a8846137bb565b558651805f80516020615bb8833981519152958693829190602083019252565b0390a383519283523092602090a3519283923084613979565b8551633a94626760e11b8152600490fd5b84516363e00e3360e11b81526004810191909152602490fd5b61551a915060203d81116109cf576109c18183610541565b5f6153be565b92602083829495845f61553d61091560075460018060a01b031690565b9261555f6040519788968795869463d02f735160e01b86523060048701615018565b03925af19081156109a3575f91615605575b50806155eb57506001600160a01b0381811693908116928385146155d9575f80516020615bb883398151915292826155b261548884610f8d6155d4976137bb565b556155c3615488836126a8846137bb565b556040519081529081906020820190565b0390a3565b604051633a94626760e11b8152600490fd5b6040516363e00e3360e11b81526004810191909152602490fd5b61561d915060203d81116109cf576109c18183610541565b5f615571565b6001600160a01b0391821681529116602082015260400190565b1561564457565b60405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c7365000000006044820152606490fd5b60055461569e906001600160a01b0316610915565b3303615753576007546001600160a01b0316604051623f1ee960e11b815291906020836004816001600160a01b0386165afa9283156109a3577f7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d9361570a915f91615735575b5061563d565b600780546001600160a01b0319166001600160a01b0384161790555b61364160405192839283615623565b61574d915060203d8111611179576111698183610541565b5f615704565b60405163d219dc1f60e01b8152600490fd5b60055461577a906001600160a01b0316610915565b330361582157600b54430361580f576008546001600160a01b03166040516310c8fc9560e11b815291906020836004816001600160a01b0386165afa9283156109a3577fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926936157ef915f91615735575061563d565b600880546001600160a01b0319166001600160a01b038416179055615726565b604051630be2a5cb60e11b8152600490fd5b60405163407fded560e01b8152600490fd5b9061584c915f60405161584581610526565b525161463a565b6040519061585982610526565b815290565b5115614657575f90565b670de0b6b3a7640000908181029181830414901517156115795761341191519061464d565b6158c26113f193926158b460405194859263a9059cbb60e01b602085015260248401613314565b03601f198101845283610541565b6158ef565b906158c2906158b46113f1956040519586936323b872dd60e01b602086015260248501613b47565b5f806159379260018060a01b03169360208151910182865af13d15615992573d90615919826113f3565b916159276040519384610541565b82523d5f602084013e5b8361599a565b8051908115159182615970575b505061594d5750565b604051635274afe760e01b81526001600160a01b03919091166004820152602490fd5b61598b925090602080615987938301019101613763565b1590565b5f80615944565b606090615931565b906159c157508051156159af57805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806159f4575b6159d2575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156159ca565b634e487b7160e01b5f52603260045260245ffd5b601754811015615a295760175f5260205f2001905f90565b6159fd565b5f81815260186020526040812054615a9b5760175490600160401b821015610521576001820180601755821015615a29577fc624b66cc0138b8fabc209247f72d758e1cf3343756d543badbf24212bed8c1590910182905560175491815260186020526040902055600190565b905090565b6017548015615aca575f19818101919081831015615a29575f916017835260208320010155601755565b634e487b7160e01b5f52603160045260245ffd5b5f81815260186020526040902054801561474c575f199181830191808311611579576017549384019384116115795783835f95615b349503615b3a575b505050615b26615aa0565b5f52601860205260405f2090565b55600190565b615b26615b6291615b5a615b50615b7a95615a11565b90549060031b1c90565b928391615a11565b90919082549060031b91821b915f19901b1916179055565b555f8080615b1b565b601754811015615a295760175f527fc624b66cc0138b8fabc209247f72d758e1cf3343756d543badbf24212bed8c1501549056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122073863b51884c983baa49ccbe8b41f9c5b038c6b7f31de68bd6f18930afb3a4b464736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000e5da20f15420ad15de0fa650600afc998bbe3955000000000000000000000000d129538727c0a40197f01ff04e4eafe2c28a10b700000000000000000000000087108936931548f3fe42c148c5ff9dab40e63a38000000000000000000000000000000000000000000a56fa5b99019a5c8000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047786ac88f8cdb8d3a5fe956e51f95b4ae9636fa00000000000000000000000071e2fd817e57fe853b46d51b1f713ef03eaaae12000000000000000000000000000000000000000000000000000000000000000b724574682043546f6b656e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000056372457468000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : underlying_ (address): 0xE5DA20F15420aD15DE0fa650600aFc998bbE3955
Arg [1] : comptroller_ (address): 0xD129538727C0a40197F01FF04E4EafE2C28a10B7
Arg [2] : interestRateModel_ (address): 0x87108936931548F3fe42c148C5Ff9daB40e63A38
Arg [3] : initialExchangeRateMantissa_ (uint256): 200000000000000000000000000
Arg [4] : name_ (string): rEth CToken
Arg [5] : symbol_ (string): crEth
Arg [6] : decimals_ (uint8): 8
Arg [7] : fullUtilizationRate_ (uint256): 0
Arg [8] : admin_ (address): 0x47786AC88f8CDB8D3A5FE956e51F95b4aE9636fa
Arg [9] : _vault (address): 0x71e2fD817e57FE853B46D51B1F713eF03EAaae12
-----Encoded View---------------
14 Constructor Arguments found :
Arg [0] : 000000000000000000000000e5da20f15420ad15de0fa650600afc998bbe3955
Arg [1] : 000000000000000000000000d129538727c0a40197f01ff04e4eafe2c28a10b7
Arg [2] : 00000000000000000000000087108936931548f3fe42c148c5ff9dab40e63a38
Arg [3] : 000000000000000000000000000000000000000000a56fa5b99019a5c8000000
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000140
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000180
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000008
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [8] : 00000000000000000000000047786ac88f8cdb8d3a5fe956e51f95b4ae9636fa
Arg [9] : 00000000000000000000000071e2fd817e57fe853b46d51b1f713ef03eaaae12
Arg [10] : 000000000000000000000000000000000000000000000000000000000000000b
Arg [11] : 724574682043546f6b656e000000000000000000000000000000000000000000
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [13] : 6372457468000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in S
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.