Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
AlchemistV2
Compiler Version
v0.8.13+commit.abaa5c0e
Optimization Enabled:
Yes with 200 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: Unlicense pragma solidity ^0.8.13; import {Initializable} from "../lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol"; import {Unauthorized, IllegalState, IllegalArgument} from "./base/Errors.sol"; import "./base/Multicall.sol"; import "./base/Mutex.sol"; import "./interfaces/IAlchemistV2.sol"; import "./interfaces/IERC20TokenReceiver.sol"; import "./interfaces/ITokenAdapter.sol"; import "./interfaces/IAlchemicToken.sol"; import "./interfaces/IWhitelist.sol"; import "./interfaces/IRewardCollector.sol"; import "./interfaces/external/vesper/IVesperRewards.sol"; import "./interfaces/external/vesper/IVesperPool.sol"; import "./libraries/SafeCast.sol"; import "./libraries/Sets.sol"; import "./libraries/TokenUtils.sol"; import "./libraries/Limiters.sol"; /// @title AlchemistV2 /// @author Alchemix Finance contract AlchemistV2 is IAlchemistV2, Initializable, Multicall, Mutex { using Limiters for Limiters.LinearGrowthLimiter; using Sets for Sets.AddressSet; /// @notice A user account. struct Account { // A signed value which represents the current amount of debt or credit that the account has accrued. // Positive values indicate debt, negative values indicate credit. int256 debt; // The share balances for each yield token. mapping(address => uint256) balances; // The last values recorded for accrued weights for each yield token. mapping(address => uint256) lastAccruedWeights; // The set of yield tokens that the account has deposited into the system. Sets.AddressSet depositedTokens; // The allowances for mints. mapping(address => uint256) mintAllowances; // The allowances for withdrawals. mapping(address => mapping(address => uint256)) withdrawAllowances; } /// @notice The number of basis points there are to represent exactly 100%. uint256 public constant BPS = 10_000; /// @notice The scalar used for conversion of integral numbers to fixed point numbers. Fixed point numbers in this /// implementation have 18 decimals of resolution, meaning that 1 is represented as 1e18, 0.5 is /// represented as 5e17, and 2 is represented as 2e18. uint256 public constant FIXED_POINT_SCALAR = 1e18; /// @inheritdoc IAlchemistV2Immutables string public constant override version = "2.2.8"; /// @inheritdoc IAlchemistV2Immutables address public override debtToken; /// @inheritdoc IAlchemistV2State address public override admin; /// @inheritdoc IAlchemistV2State address public override pendingAdmin; /// @inheritdoc IAlchemistV2State mapping(address => bool) public override sentinels; /// @inheritdoc IAlchemistV2State mapping(address => bool) public override keepers; /// @inheritdoc IAlchemistV2State address public override transmuter; /// @inheritdoc IAlchemistV2State uint256 public override minimumCollateralization; /// @inheritdoc IAlchemistV2State uint256 public override protocolFee; /// @inheritdoc IAlchemistV2State address public override protocolFeeReceiver; /// @inheritdoc IAlchemistV2State address public override whitelist; /// @dev A linear growth function that limits the amount of debt-token minted. Limiters.LinearGrowthLimiter private _mintingLimiter; // @dev The repay limiters for each underlying token. mapping(address => Limiters.LinearGrowthLimiter) private _repayLimiters; // @dev The liquidation limiters for each underlying token. mapping(address => Limiters.LinearGrowthLimiter) private _liquidationLimiters; /// @dev Accounts mapped by the address that owns them. mapping(address => Account) private _accounts; /// @dev Underlying token parameters mapped by token address. mapping(address => UnderlyingTokenParams) private _underlyingTokens; /// @dev Yield token parameters mapped by token address. mapping(address => YieldTokenParams) private _yieldTokens; /// @dev An iterable set of the underlying tokens that are supported by the system. Sets.AddressSet private _supportedUnderlyingTokens; /// @dev An iterable set of the yield tokens that are supported by the system. Sets.AddressSet private _supportedYieldTokens; /// @inheritdoc IAlchemistV2State address public override transferAdapter; constructor() initializer {} /// @inheritdoc IAlchemistV2State function getYieldTokensPerShare(address yieldToken) external view override returns (uint256) { return convertSharesToYieldTokens(yieldToken, 10**_yieldTokens[yieldToken].decimals); } /// @inheritdoc IAlchemistV2State function getUnderlyingTokensPerShare(address yieldToken) external view override returns (uint256) { return convertSharesToUnderlyingTokens(yieldToken, 10**_yieldTokens[yieldToken].decimals); } /// @inheritdoc IAlchemistV2State function getSupportedUnderlyingTokens() external view override returns (address[] memory) { return _supportedUnderlyingTokens.values; } /// @inheritdoc IAlchemistV2State function getSupportedYieldTokens() external view override returns (address[] memory) { return _supportedYieldTokens.values; } /// @inheritdoc IAlchemistV2State function isSupportedUnderlyingToken(address underlyingToken) external view override returns (bool) { return _supportedUnderlyingTokens.contains(underlyingToken); } /// @inheritdoc IAlchemistV2State function isSupportedYieldToken(address yieldToken) external view override returns (bool) { return _supportedYieldTokens.contains(yieldToken); } /// @inheritdoc IAlchemistV2State function accounts(address owner) external view override returns ( int256 debt, address[] memory depositedTokens ) { Account storage account = _accounts[owner]; return ( _calculateUnrealizedDebt(owner), account.depositedTokens.values ); } /// @inheritdoc IAlchemistV2State function positions(address owner, address yieldToken) external view override returns ( uint256 shares, uint256 lastAccruedWeight ) { Account storage account = _accounts[owner]; return (account.balances[yieldToken], account.lastAccruedWeights[yieldToken]); } /// @inheritdoc IAlchemistV2State function mintAllowance(address owner, address spender) external view override returns (uint256) { Account storage account = _accounts[owner]; return account.mintAllowances[spender]; } /// @inheritdoc IAlchemistV2State function withdrawAllowance(address owner, address spender, address yieldToken) external view override returns (uint256) { Account storage account = _accounts[owner]; return account.withdrawAllowances[spender][yieldToken]; } /// @inheritdoc IAlchemistV2State function getUnderlyingTokenParameters(address underlyingToken) external view override returns (UnderlyingTokenParams memory) { return _underlyingTokens[underlyingToken]; } /// @inheritdoc IAlchemistV2State function getYieldTokenParameters(address yieldToken) external view override returns (YieldTokenParams memory) { return _yieldTokens[yieldToken]; } /// @inheritdoc IAlchemistV2State function getMintLimitInfo() external view override returns ( uint256 currentLimit, uint256 rate, uint256 maximum ) { return ( _mintingLimiter.get(), _mintingLimiter.rate, _mintingLimiter.maximum ); } /// @inheritdoc IAlchemistV2State function getRepayLimitInfo(address underlyingToken) external view override returns ( uint256 currentLimit, uint256 rate, uint256 maximum ) { Limiters.LinearGrowthLimiter storage limiter = _repayLimiters[underlyingToken]; return ( limiter.get(), limiter.rate, limiter.maximum ); } /// @inheritdoc IAlchemistV2State function getLiquidationLimitInfo(address underlyingToken) external view override returns ( uint256 currentLimit, uint256 rate, uint256 maximum ) { Limiters.LinearGrowthLimiter storage limiter = _liquidationLimiters[underlyingToken]; return ( limiter.get(), limiter.rate, limiter.maximum ); } /// @inheritdoc IAlchemistV2AdminActions function initialize(InitializationParams memory params) external initializer { _checkArgument(params.protocolFee <= BPS); debtToken = params.debtToken; admin = params.admin; transmuter = params.transmuter; minimumCollateralization = params.minimumCollateralization; protocolFee = params.protocolFee; protocolFeeReceiver = params.protocolFeeReceiver; whitelist = params.whitelist; _mintingLimiter = Limiters.createLinearGrowthLimiter( params.mintingLimitMaximum, params.mintingLimitBlocks, params.mintingLimitMinimum ); emit AdminUpdated(admin); emit TransmuterUpdated(transmuter); emit MinimumCollateralizationUpdated(minimumCollateralization); emit ProtocolFeeUpdated(protocolFee); emit MintingLimitUpdated(params.mintingLimitMaximum, params.mintingLimitBlocks); } /// @inheritdoc IAlchemistV2AdminActions function setPendingAdmin(address value) external override { _onlyAdmin(); pendingAdmin = value; emit PendingAdminUpdated(value); } /// @inheritdoc IAlchemistV2AdminActions function acceptAdmin() external override { _checkState(pendingAdmin != address(0)); if (msg.sender != pendingAdmin) { revert Unauthorized(); } admin = pendingAdmin; pendingAdmin = address(0); emit AdminUpdated(admin); emit PendingAdminUpdated(address(0)); } /// @inheritdoc IAlchemistV2AdminActions function setSentinel(address sentinel, bool flag) external override { _onlyAdmin(); sentinels[sentinel] = flag; emit SentinelSet(sentinel, flag); } /// @inheritdoc IAlchemistV2AdminActions function setKeeper(address keeper, bool flag) external override { _onlyAdmin(); keepers[keeper] = flag; emit KeeperSet(keeper, flag); } /// @inheritdoc IAlchemistV2AdminActions function addUnderlyingToken(address underlyingToken, UnderlyingTokenConfig calldata config) external override lock { _onlyAdmin(); _checkState(!_supportedUnderlyingTokens.contains(underlyingToken)); uint8 tokenDecimals = TokenUtils.expectDecimals(underlyingToken); uint8 debtTokenDecimals = TokenUtils.expectDecimals(debtToken); _checkArgument(tokenDecimals <= debtTokenDecimals); _underlyingTokens[underlyingToken] = UnderlyingTokenParams({ decimals: tokenDecimals, conversionFactor: 10**(debtTokenDecimals - tokenDecimals), enabled: false }); _repayLimiters[underlyingToken] = Limiters.createLinearGrowthLimiter( config.repayLimitMaximum, config.repayLimitBlocks, config.repayLimitMinimum ); _liquidationLimiters[underlyingToken] = Limiters.createLinearGrowthLimiter( config.liquidationLimitMaximum, config.liquidationLimitBlocks, config.liquidationLimitMinimum ); _supportedUnderlyingTokens.add(underlyingToken); emit AddUnderlyingToken(underlyingToken); } /// @inheritdoc IAlchemistV2AdminActions function addYieldToken(address yieldToken, YieldTokenConfig calldata config) external override lock { _onlyAdmin(); _checkArgument(config.maximumLoss <= BPS); _checkArgument(config.creditUnlockBlocks > 0); _checkState(!_supportedYieldTokens.contains(yieldToken)); ITokenAdapter adapter = ITokenAdapter(config.adapter); _checkState(yieldToken == adapter.token()); _checkSupportedUnderlyingToken(adapter.underlyingToken()); _yieldTokens[yieldToken] = YieldTokenParams({ decimals: TokenUtils.expectDecimals(yieldToken), underlyingToken: adapter.underlyingToken(), adapter: config.adapter, maximumLoss: config.maximumLoss, maximumExpectedValue: config.maximumExpectedValue, creditUnlockRate: FIXED_POINT_SCALAR / config.creditUnlockBlocks, activeBalance: 0, harvestableBalance: 0, totalShares: 0, expectedValue: 0, accruedWeight: 0, pendingCredit: 0, distributedCredit: 0, lastDistributionBlock: 0, enabled: false }); _supportedYieldTokens.add(yieldToken); TokenUtils.safeApprove(yieldToken, config.adapter, type(uint256).max); TokenUtils.safeApprove(adapter.underlyingToken(), config.adapter, type(uint256).max); emit AddYieldToken(yieldToken); emit TokenAdapterUpdated(yieldToken, config.adapter); emit MaximumLossUpdated(yieldToken, config.maximumLoss); } /// @inheritdoc IAlchemistV2AdminActions function setUnderlyingTokenEnabled(address underlyingToken, bool enabled) external override { _onlySentinelOrAdmin(); _checkSupportedUnderlyingToken(underlyingToken); _underlyingTokens[underlyingToken].enabled = enabled; emit UnderlyingTokenEnabled(underlyingToken, enabled); } /// @inheritdoc IAlchemistV2AdminActions function setYieldTokenEnabled(address yieldToken, bool enabled) external override { _onlySentinelOrAdmin(); _checkSupportedYieldToken(yieldToken); _yieldTokens[yieldToken].enabled = enabled; emit YieldTokenEnabled(yieldToken, enabled); } /// @inheritdoc IAlchemistV2AdminActions function configureRepayLimit(address underlyingToken, uint256 maximum, uint256 blocks) external override { _onlyAdmin(); _checkSupportedUnderlyingToken(underlyingToken); _repayLimiters[underlyingToken].update(); _repayLimiters[underlyingToken].configure(maximum, blocks); emit RepayLimitUpdated(underlyingToken, maximum, blocks); } /// @inheritdoc IAlchemistV2AdminActions function configureLiquidationLimit(address underlyingToken, uint256 maximum, uint256 blocks) external override { _onlyAdmin(); _checkSupportedUnderlyingToken(underlyingToken); _liquidationLimiters[underlyingToken].update(); _liquidationLimiters[underlyingToken].configure(maximum, blocks); emit LiquidationLimitUpdated(underlyingToken, maximum, blocks); } /// @inheritdoc IAlchemistV2AdminActions function setTransmuter(address value) external override { _onlyAdmin(); _checkArgument(value != address(0)); transmuter = value; emit TransmuterUpdated(value); } /// @inheritdoc IAlchemistV2AdminActions function setMinimumCollateralization(uint256 value) external override { _onlyAdmin(); _checkArgument(value >= 1e18); minimumCollateralization = value; emit MinimumCollateralizationUpdated(value); } /// @inheritdoc IAlchemistV2AdminActions function setProtocolFee(uint256 value) external override { _onlyAdmin(); _checkArgument(value <= BPS); protocolFee = value; emit ProtocolFeeUpdated(value); } /// @inheritdoc IAlchemistV2AdminActions function setProtocolFeeReceiver(address value) external override { _onlyAdmin(); _checkArgument(value != address(0)); protocolFeeReceiver = value; emit ProtocolFeeReceiverUpdated(value); } /// @inheritdoc IAlchemistV2AdminActions function configureMintingLimit(uint256 maximum, uint256 rate) external override { _onlyAdmin(); _mintingLimiter.update(); _mintingLimiter.configure(maximum, rate); emit MintingLimitUpdated(maximum, rate); } /// @inheritdoc IAlchemistV2AdminActions function configureCreditUnlockRate(address yieldToken, uint256 blocks) external override { _onlyAdmin(); _checkArgument(blocks > 0); _checkSupportedYieldToken(yieldToken); _yieldTokens[yieldToken].creditUnlockRate = FIXED_POINT_SCALAR / blocks; emit CreditUnlockRateUpdated(yieldToken, blocks); } /// @inheritdoc IAlchemistV2AdminActions function setTokenAdapter(address yieldToken, address adapter) external override { _onlyAdmin(); _checkState(yieldToken == ITokenAdapter(adapter).token()); _checkSupportedYieldToken(yieldToken); _yieldTokens[yieldToken].adapter = adapter; TokenUtils.safeApprove(yieldToken, adapter, type(uint256).max); TokenUtils.safeApprove(ITokenAdapter(adapter).underlyingToken(), adapter, type(uint256).max); emit TokenAdapterUpdated(yieldToken, adapter); } /// @inheritdoc IAlchemistV2AdminActions function setMaximumExpectedValue(address yieldToken, uint256 value) external override { _onlyAdmin(); _checkSupportedYieldToken(yieldToken); _yieldTokens[yieldToken].maximumExpectedValue = value; emit MaximumExpectedValueUpdated(yieldToken, value); } /// @inheritdoc IAlchemistV2AdminActions function setMaximumLoss(address yieldToken, uint256 value) external override { _onlyAdmin(); _checkArgument(value <= BPS); _checkSupportedYieldToken(yieldToken); _yieldTokens[yieldToken].maximumLoss = value; emit MaximumLossUpdated(yieldToken, value); } /// @inheritdoc IAlchemistV2AdminActions function snap(address yieldToken) external override lock { _onlyAdmin(); _checkSupportedYieldToken(yieldToken); uint256 expectedValue = convertYieldTokensToUnderlying(yieldToken, _yieldTokens[yieldToken].activeBalance); _yieldTokens[yieldToken].expectedValue = expectedValue; emit Snap(yieldToken, expectedValue); } /// @inheritdoc IAlchemistV2AdminActions function setTransferAdapterAddress(address transferAdapterAddress) external override lock { _onlyAdmin(); transferAdapter = transferAdapterAddress; } /// @inheritdoc IAlchemistV2AdminActions function transferDebtV1( address owner, int256 debt ) external override lock { _onlyTransferAdapter(); _poke(owner); _updateDebt(owner, debt); _validate(owner); } /// @inheritdoc IAlchemistV2Actions function approveMint(address spender, uint256 amount) external override { _onlyWhitelisted(); _approveMint(msg.sender, spender, amount); } /// @inheritdoc IAlchemistV2Actions function approveWithdraw(address spender, address yieldToken, uint256 shares) external override { _onlyWhitelisted(); _checkSupportedYieldToken(yieldToken); _approveWithdraw(msg.sender, spender, yieldToken, shares); } /// @inheritdoc IAlchemistV2Actions function poke(address owner) external override lock { _onlyWhitelisted(); _preemptivelyHarvestDeposited(owner); _distributeUnlockedCreditDeposited(owner); _poke(owner); } /// @inheritdoc IAlchemistV2Actions function deposit( address yieldToken, uint256 amount, address recipient ) external override lock returns (uint256) { _onlyWhitelisted(); _checkArgument(recipient != address(0)); _checkSupportedYieldToken(yieldToken); // Deposit the yield tokens to the recipient. uint256 shares = _deposit(yieldToken, amount, recipient); // Transfer tokens from the message sender now that the internal storage updates have been committed. TokenUtils.safeTransferFrom(yieldToken, msg.sender, address(this), amount); return shares; } /// @inheritdoc IAlchemistV2Actions function depositUnderlying( address yieldToken, uint256 amount, address recipient, uint256 minimumAmountOut ) external override lock returns (uint256) { _onlyWhitelisted(); _checkArgument(recipient != address(0)); _checkSupportedYieldToken(yieldToken); // Before depositing, the underlying tokens must be wrapped into yield tokens. uint256 amountYieldTokens = _wrap(yieldToken, amount, minimumAmountOut); // Deposit the yield-tokens to the recipient. return _deposit(yieldToken, amountYieldTokens, recipient); } /// @inheritdoc IAlchemistV2Actions function withdraw( address yieldToken, uint256 shares, address recipient ) external override lock returns (uint256) { _onlyWhitelisted(); _checkArgument(recipient != address(0)); _checkSupportedYieldToken(yieldToken); // Withdraw the shares from the system. uint256 amountYieldTokens = _withdraw(yieldToken, msg.sender, shares, recipient); // Transfer the yield tokens to the recipient. TokenUtils.safeTransfer(yieldToken, recipient, amountYieldTokens); return amountYieldTokens; } /// @inheritdoc IAlchemistV2Actions function withdrawFrom( address owner, address yieldToken, uint256 shares, address recipient ) external override lock returns (uint256) { _onlyWhitelisted(); _checkArgument(recipient != address(0)); _checkSupportedYieldToken(yieldToken); // Preemptively try and decrease the withdrawal allowance. This will save gas when the allowance is not // sufficient for the withdrawal. _decreaseWithdrawAllowance(owner, msg.sender, yieldToken, shares); // Withdraw the shares from the system. uint256 amountYieldTokens = _withdraw(yieldToken, owner, shares, recipient); // Transfer the yield tokens to the recipient. TokenUtils.safeTransfer(yieldToken, recipient, amountYieldTokens); return amountYieldTokens; } /// @inheritdoc IAlchemistV2Actions function withdrawUnderlying( address yieldToken, uint256 shares, address recipient, uint256 minimumAmountOut ) external override lock returns (uint256) { _onlyWhitelisted(); _checkArgument(recipient != address(0)); _checkSupportedYieldToken(yieldToken); _checkLoss(yieldToken); uint256 amountYieldTokens = _withdraw(yieldToken, msg.sender, shares, recipient); return _unwrap(yieldToken, amountYieldTokens, recipient, minimumAmountOut); } /// @inheritdoc IAlchemistV2Actions function withdrawUnderlyingFrom( address owner, address yieldToken, uint256 shares, address recipient, uint256 minimumAmountOut ) external override lock returns (uint256) { _onlyWhitelisted(); _checkArgument(recipient != address(0)); _checkSupportedYieldToken(yieldToken); _checkLoss(yieldToken); _decreaseWithdrawAllowance(owner, msg.sender, yieldToken, shares); uint256 amountYieldTokens = _withdraw(yieldToken, owner, shares, recipient); return _unwrap(yieldToken, amountYieldTokens, recipient, minimumAmountOut); } /// @inheritdoc IAlchemistV2Actions function mint(uint256 amount, address recipient) external override lock { _onlyWhitelisted(); _checkArgument(amount > 0); _checkArgument(recipient != address(0)); // Mint tokens from the message sender's account to the recipient. _mint(msg.sender, amount, recipient); } /// @inheritdoc IAlchemistV2Actions function mintFrom( address owner, uint256 amount, address recipient ) external override lock { _onlyWhitelisted(); _checkArgument(amount > 0); _checkArgument(recipient != address(0)); // Preemptively try and decrease the minting allowance. This will save gas when the allowance is not sufficient // for the mint. _decreaseMintAllowance(owner, msg.sender, amount); // Mint tokens from the owner's account to the recipient. _mint(owner, amount, recipient); } /// @inheritdoc IAlchemistV2Actions function burn(uint256 amount, address recipient) external override lock returns (uint256) { _onlyWhitelisted(); _checkArgument(amount > 0); _checkArgument(recipient != address(0)); // Distribute unlocked credit to depositors. _distributeUnlockedCreditDeposited(recipient); // Update the recipient's account, decrease the debt of the recipient by the number of tokens burned. _poke(recipient); // Check that the debt is greater than zero. // // It is possible that the number of debt which is repayable is equal to or less than zero after realizing the // credit that was earned since the last update. We do not want to perform a noop so we need to check that the // amount of debt to repay is greater than zero. int256 debt; _checkState((debt = _accounts[recipient].debt) > 0); // Limit how much debt can be repaid up to the current amount of debt that the account has. This prevents // situations where the user may be trying to repay their entire debt, but it decreases since they send the // transaction and causes a revert because burning can never decrease the debt below zero. // // Casts here are safe because it is asserted that debt is greater than zero. uint256 credit = amount > uint256(debt) ? uint256(debt) : amount; // Update the recipient's debt. _updateDebt(recipient, -SafeCast.toInt256(credit)); // Burn the tokens from the message sender. TokenUtils.safeBurnFrom(debtToken, msg.sender, credit); // Increase the global amount of mintable debt tokens. // Do this after burning instead of before because mint limit increase is an action beneficial to the user. _mintingLimiter.increase(credit); emit Burn(msg.sender, credit, recipient); return credit; } /// @inheritdoc IAlchemistV2Actions function repay(address underlyingToken, uint256 amount, address recipient) external override lock returns (uint256) { _onlyWhitelisted(); _checkArgument(amount > 0); _checkArgument(recipient != address(0)); _checkSupportedUnderlyingToken(underlyingToken); _checkUnderlyingTokenEnabled(underlyingToken); // Distribute unlocked credit to depositors. _distributeUnlockedCreditDeposited(recipient); // Update the recipient's account and decrease the amount of debt incurred. _poke(recipient); // Check that the debt is greater than zero. // // It is possible that the amount of debt which is repayable is equal to or less than zero after realizing the // credit that was earned since the last update. We do not want to perform a noop so we need to check that the // amount of debt to repay is greater than zero. int256 debt; _checkState((debt = _accounts[recipient].debt) > 0); // Determine the maximum amount of underlying tokens that can be repaid. // // It is implied that this value is greater than zero because `debt` is greater than zero so a noop is not possible // beyond this point. Casting the debt to an unsigned integer is also safe because `debt` is greater than zero. uint256 maximumAmount = normalizeDebtTokensToUnderlying(underlyingToken, uint256(debt)); // Limit the number of underlying tokens to repay up to the maximum allowed. uint256 actualAmount = amount > maximumAmount ? maximumAmount : amount; Limiters.LinearGrowthLimiter storage limiter = _repayLimiters[underlyingToken]; // Check to make sure that the underlying token repay limit has not been breached. uint256 currentRepayLimit = limiter.get(); if (actualAmount > currentRepayLimit) { revert RepayLimitExceeded(underlyingToken, actualAmount, currentRepayLimit); } uint256 credit = normalizeUnderlyingTokensToDebt(underlyingToken, actualAmount); // Update the recipient's debt. _updateDebt(recipient, -SafeCast.toInt256(credit)); // Decrease the amount of the underlying token which is globally available to be repaid. limiter.decrease(actualAmount); // Transfer the repaid tokens to the transmuter. TokenUtils.safeTransferFrom(underlyingToken, msg.sender, transmuter, actualAmount); // Inform the transmuter that it has received tokens. IERC20TokenReceiver(transmuter).onERC20Received(underlyingToken, actualAmount); emit Repay(msg.sender, underlyingToken, actualAmount, recipient, credit); return actualAmount; } /// @inheritdoc IAlchemistV2Actions function liquidate( address yieldToken, uint256 shares, uint256 minimumAmountOut ) external override lock returns (uint256) { _onlyWhitelisted(); _checkArgument(shares > 0); YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; address underlyingToken = yieldTokenParams.underlyingToken; _checkSupportedYieldToken(yieldToken); _checkYieldTokenEnabled(yieldToken); _checkUnderlyingTokenEnabled(underlyingToken); _checkLoss(yieldToken); // Calculate the unrealized debt. // // It is possible that the number of debt which is repayable is equal to or less than zero after realizing the // credit that was earned since the last update. We do not want to perform a noop so we need to check that the // amount of debt to repay is greater than zero. int256 unrealizedDebt; _checkState((unrealizedDebt = _calculateUnrealizedDebt(msg.sender)) > 0); // Determine the maximum amount of shares that can be liquidated from the unrealized debt. // // It is implied that this value is greater than zero because `debt` is greater than zero. Casting the debt to an // unsigned integer is also safe for this reason. uint256 maximumShares = convertUnderlyingTokensToShares( yieldToken, normalizeDebtTokensToUnderlying(underlyingToken, uint256(unrealizedDebt)) ); // Limit the number of shares to liquidate up to the maximum allowed. uint256 actualShares = shares > maximumShares ? maximumShares : shares; // Unwrap the yield tokens that the shares are worth. uint256 amountYieldTokens = convertSharesToYieldTokens(yieldToken, actualShares); uint256 amountUnderlyingTokens = _unwrap(yieldToken, amountYieldTokens, address(this), minimumAmountOut); // Again, perform another noop check. It is possible that the amount of underlying tokens that were received by // unwrapping the yield tokens was zero because the amount of yield tokens to unwrap was too small. _checkState(amountUnderlyingTokens > 0); Limiters.LinearGrowthLimiter storage limiter = _liquidationLimiters[underlyingToken]; // Check to make sure that the underlying token liquidation limit has not been breached. uint256 liquidationLimit = limiter.get(); if (amountUnderlyingTokens > liquidationLimit) { revert LiquidationLimitExceeded(underlyingToken, amountUnderlyingTokens, liquidationLimit); } // Buffers any harvestable yield tokens. This will properly synchronize the balance which is held by users // and the balance which is held by the system. This is required for `_sync` to function correctly. _preemptivelyHarvest(yieldToken); // Distribute unlocked credit to depositors. _distributeUnlockedCreditDeposited(msg.sender); uint256 credit = normalizeUnderlyingTokensToDebt(underlyingToken, amountUnderlyingTokens); // Update the message sender's account, proactively burn shares, decrease the amount of debt incurred, and then // decrease the value of the token that the system is expected to hold. _poke(msg.sender, yieldToken); _burnShares(msg.sender, yieldToken, actualShares); _updateDebt(msg.sender, -SafeCast.toInt256(credit)); _sync(yieldToken, amountYieldTokens, _usub); // Decrease the amount of the underlying token which is globally available to be liquidated. limiter.decrease(amountUnderlyingTokens); // Transfer the liquidated tokens to the transmuter. TokenUtils.safeTransfer(underlyingToken, transmuter, amountUnderlyingTokens); // Inform the transmuter that it has received tokens. IERC20TokenReceiver(transmuter).onERC20Received(underlyingToken, amountUnderlyingTokens); // In the case that slippage allowed by minimumAmountOut would create an undercollateralized position _validate(msg.sender); emit Liquidate(msg.sender, yieldToken, underlyingToken, actualShares, credit); return actualShares; } /// @inheritdoc IAlchemistV2Actions function donate(address yieldToken, uint256 amount) external override lock { _onlyWhitelisted(); _checkArgument(amount > 0); // Distribute any unlocked credit so that the accrued weight is up to date. _distributeUnlockedCredit(yieldToken); // Update the message sender's account. This will assure that any credit that was earned is not overridden. _poke(msg.sender); uint256 shares = _yieldTokens[yieldToken].totalShares - _accounts[msg.sender].balances[yieldToken]; _yieldTokens[yieldToken].accruedWeight += amount * FIXED_POINT_SCALAR / shares; _accounts[msg.sender].lastAccruedWeights[yieldToken] = _yieldTokens[yieldToken].accruedWeight; TokenUtils.safeBurnFrom(debtToken, msg.sender, amount); // Increase the global amount of mintable debt tokens. // Do this after burning instead of before because mint limit increase is an action beneficial to the user. _mintingLimiter.increase(amount); emit Donate(msg.sender, yieldToken, amount); } /// @inheritdoc IAlchemistV2Actions function harvest(address yieldToken, uint256 minimumAmountOut) external override lock { _onlyKeeper(); _checkSupportedYieldToken(yieldToken); YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; // Buffer any harvestable yield tokens. This will properly synchronize the balance which is held by users // and the balance which is held by the system to be harvested during this call. _preemptivelyHarvest(yieldToken); // Load and proactively clear the amount of harvestable tokens so that future calls do not rely on stale data. // Because we cannot call an external unwrap until the amount of harvestable tokens has been calculated, // clearing this data immediately prevents any potential reentrancy attacks which would use stale harvest // buffer values. uint256 harvestableAmount = yieldTokenParams.harvestableBalance; yieldTokenParams.harvestableBalance = 0; // Check that the harvest will not be a no-op. _checkState(harvestableAmount != 0); address underlyingToken = yieldTokenParams.underlyingToken; uint256 amountUnderlyingTokens = _unwrap(yieldToken, harvestableAmount, address(this), minimumAmountOut); // Calculate how much of the unwrapped underlying tokens will be allocated for fees and distributed to users. uint256 feeAmount = amountUnderlyingTokens * protocolFee / BPS; uint256 distributeAmount = amountUnderlyingTokens - feeAmount; uint256 credit = normalizeUnderlyingTokensToDebt(underlyingToken, distributeAmount); // Distribute credit to all of the users who hold shares of the yield token. _distributeCredit(yieldToken, credit); // Transfer the tokens to the fee receiver and transmuter. TokenUtils.safeTransfer(underlyingToken, protocolFeeReceiver, feeAmount); TokenUtils.safeTransfer(underlyingToken, transmuter, distributeAmount); // Inform the transmuter that it has received tokens. IERC20TokenReceiver(transmuter).onERC20Received(underlyingToken, distributeAmount); emit Harvest(yieldToken, minimumAmountOut, amountUnderlyingTokens, credit); } /// @dev Checks that the `msg.sender` is the administrator. /// /// @dev `msg.sender` must be the administrator or this call will revert with an {Unauthorized} error. function _onlyAdmin() internal view { if (msg.sender != admin) { revert Unauthorized(); } } /// @dev Checks that the `msg.sender` is the administrator or a sentinel. /// /// @dev `msg.sender` must be either the administrator or a sentinel or this call will revert with an /// {Unauthorized} error. function _onlySentinelOrAdmin() internal view { // Check if the message sender is the administrator. if (msg.sender == admin) { return; } // Check if the message sender is a sentinel. After this check we can revert since we know that it is neither // the administrator or a sentinel. if (!sentinels[msg.sender]) { revert Unauthorized(); } } /// @dev Checks that the `msg.sender` is a keeper. /// /// @dev `msg.sender` must be a keeper or this call will revert with an {Unauthorized} error. function _onlyKeeper() internal view { if (!keepers[msg.sender]) { revert Unauthorized(); } } /// @dev Checks that the `msg.sender` is the V1 transfer adapter. /// /// @dev `msg.sender` must be the administrator or this call will revert with an {Unauthorized} error. function _onlyTransferAdapter() internal view { if (msg.sender != transferAdapter) { revert Unauthorized(); } } /// @dev Preemptively harvests all of the yield tokens that have been deposited into an account. /// /// @param owner The address which owns the account. function _preemptivelyHarvestDeposited(address owner) internal { Sets.AddressSet storage depositedTokens = _accounts[owner].depositedTokens; for (uint256 i = 0; i < depositedTokens.values.length; ++i) { _preemptivelyHarvest(depositedTokens.values[i]); } } /// @dev Preemptively harvests `yieldToken`. /// /// @dev This will earmark yield tokens to be harvested at a future time when the current value of the token is /// greater than the expected value. The purpose of this function is to synchronize the balance of the yield /// token which is held by users versus tokens which will be seized by the protocol. /// /// @param yieldToken The address of the yield token to preemptively harvest. function _preemptivelyHarvest(address yieldToken) internal { uint256 activeBalance = _yieldTokens[yieldToken].activeBalance; if (activeBalance == 0) { return; } uint256 currentValue = convertYieldTokensToUnderlying(yieldToken, activeBalance); uint256 expectedValue = _yieldTokens[yieldToken].expectedValue; if (currentValue <= expectedValue) { return; } uint256 harvestable = convertUnderlyingTokensToYield(yieldToken, currentValue - expectedValue); if (harvestable == 0) { return; } _yieldTokens[yieldToken].activeBalance -= harvestable; _yieldTokens[yieldToken].harvestableBalance += harvestable; } /// @dev Checks if a yield token is enabled. /// /// @param yieldToken The address of the yield token. function _checkYieldTokenEnabled(address yieldToken) internal view { if (!_yieldTokens[yieldToken].enabled) { revert TokenDisabled(yieldToken); } } /// @dev Checks if an underlying token is enabled. /// /// @param underlyingToken The address of the underlying token. function _checkUnderlyingTokenEnabled(address underlyingToken) internal view { if (!_underlyingTokens[underlyingToken].enabled) { revert TokenDisabled(underlyingToken); } } /// @dev Checks if an address is a supported yield token. /// /// If the address is not a supported yield token, this function will revert using a {UnsupportedToken} error. /// /// @param yieldToken The address to check. function _checkSupportedYieldToken(address yieldToken) internal view { if (!_supportedYieldTokens.contains(yieldToken)) { revert UnsupportedToken(yieldToken); } } /// @dev Checks if an address is a supported underlying token. /// /// If the address is not a supported yield token, this function will revert using a {UnsupportedToken} error. /// /// @param underlyingToken The address to check. function _checkSupportedUnderlyingToken(address underlyingToken) internal view { if (!_supportedUnderlyingTokens.contains(underlyingToken)) { revert UnsupportedToken(underlyingToken); } } /// @dev Checks if `amount` of debt tokens can be minted. /// /// @dev `amount` must be less than the current minting limit or this call will revert with a /// {MintingLimitExceeded} error. /// /// @param amount The amount to check. function _checkMintingLimit(uint256 amount) internal view { uint256 limit = _mintingLimiter.get(); if (amount > limit) { revert MintingLimitExceeded(amount, limit); } } /// @dev Checks if the current loss of `yieldToken` has exceeded its maximum acceptable loss. /// /// @dev The loss that `yieldToken` has incurred must be less than its maximum accepted value or this call will /// revert with a {LossExceeded} error. /// /// @param yieldToken The address of the yield token. function _checkLoss(address yieldToken) internal view { uint256 loss = _loss(yieldToken); uint256 maximumLoss = _yieldTokens[yieldToken].maximumLoss; if (loss > maximumLoss) { revert LossExceeded(yieldToken, loss, maximumLoss); } } /// @dev Deposits `amount` yield tokens into the account of `recipient`. /// /// @dev Emits a {Deposit} event. /// /// @param yieldToken The address of the yield token to deposit. /// @param amount The amount of yield tokens to deposit. /// @param recipient The recipient of the yield tokens. /// /// @return The number of shares minted to `recipient`. function _deposit( address yieldToken, uint256 amount, address recipient ) internal returns (uint256) { _checkArgument(amount > 0); YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; address underlyingToken = yieldTokenParams.underlyingToken; // Check that the yield token and it's underlying token are enabled. Disabling the yield token and or the // underlying token prevents the system from holding more of the disabled yield token or underlying token. _checkYieldTokenEnabled(yieldToken); _checkUnderlyingTokenEnabled(underlyingToken); // Check to assure that the token has not experienced a sudden unexpected loss. This prevents users from being // able to deposit funds and then have them siphoned if the price recovers. _checkLoss(yieldToken); // Buffers any harvestable yield tokens. This will properly synchronize the balance which is held by users // and the balance which is held by the system to eventually be harvested. _preemptivelyHarvest(yieldToken); // Distribute unlocked credit to depositors. _distributeUnlockedCreditDeposited(recipient); // Update the recipient's account, proactively issue shares for the deposited tokens to the recipient, and then // increase the value of the token that the system is expected to hold. _poke(recipient, yieldToken); uint256 shares = _issueSharesForAmount(recipient, yieldToken, amount); _sync(yieldToken, amount, _uadd); // Check that the maximum expected value has not been breached. uint256 maximumExpectedValue = yieldTokenParams.maximumExpectedValue; if (yieldTokenParams.expectedValue > maximumExpectedValue) { revert ExpectedValueExceeded(yieldToken, amount, maximumExpectedValue); } emit Deposit(msg.sender, yieldToken, amount, recipient); return shares; } /// @dev Withdraw `yieldToken` from the account owned by `owner` by burning shares and receiving yield tokens of /// equivalent value. /// /// @dev Emits a {Withdraw} event. /// /// @param yieldToken The address of the yield token to withdraw. /// @param owner The address of the account owner to withdraw from. /// @param shares The number of shares to burn. /// @param recipient The recipient of the withdrawn shares. This parameter is only used for logging. /// /// @return The amount of yield tokens that the burned shares were exchanged for. function _withdraw( address yieldToken, address owner, uint256 shares, address recipient ) internal returns (uint256) { // Buffers any harvestable yield tokens that the owner of the account has deposited. This will properly // synchronize the balance of all the tokens held by the owner so that the validation check properly // computes the total value of the tokens held by the owner. _preemptivelyHarvestDeposited(owner); // Distribute unlocked credit for all of the tokens that the user has deposited into the system. This updates // the accrued weights so that the debt is properly calculated before the account is validated. _distributeUnlockedCreditDeposited(owner); uint256 amountYieldTokens = convertSharesToYieldTokens(yieldToken, shares); // Update the owner's account, burn shares from the owner's account, and then decrease the value of the token // that the system is expected to hold. _poke(owner); _burnShares(owner, yieldToken, shares); _sync(yieldToken, amountYieldTokens, _usub); // Valid the owner's account to assure that the collateralization invariant is still held. _validate(owner); emit Withdraw(owner, yieldToken, shares, recipient); return amountYieldTokens; } /// @dev Mints debt tokens to `recipient` using the account owned by `owner`. /// /// @dev Emits a {Mint} event. /// /// @param owner The owner of the account to mint from. /// @param amount The amount to mint. /// @param recipient The recipient of the minted debt tokens. function _mint(address owner, uint256 amount, address recipient) internal { // Check that the system will allow for the specified amount to be minted. _checkMintingLimit(amount); // Preemptively harvest all tokens that the user has deposited into the system. This allows the debt to be // properly calculated before the account is validated. _preemptivelyHarvestDeposited(owner); // Distribute unlocked credit for all of the tokens that the user has deposited into the system. This updates // the accrued weights so that the debt is properly calculated before the account is validated. _distributeUnlockedCreditDeposited(owner); // Update the owner's account, increase their debt by the amount of tokens to mint, and then finally validate // their account to assure that the collateralization invariant is still held. _poke(owner); _updateDebt(owner, SafeCast.toInt256(amount)); _validate(owner); // Decrease the global amount of mintable debt tokens. _mintingLimiter.decrease(amount); // Mint the debt tokens to the recipient. TokenUtils.safeMint(debtToken, recipient, amount); emit Mint(owner, amount, recipient); } /// @dev Synchronizes the active balance and expected value of `yieldToken`. /// /// @param yieldToken The address of the yield token. /// @param amount The amount to add or subtract from the debt. /// @param operation The mathematical operation to perform for the update. Either one of {_uadd} or {_usub}. function _sync( address yieldToken, uint256 amount, function(uint256, uint256) internal pure returns (uint256) operation ) internal { YieldTokenParams memory yieldTokenParams = _yieldTokens[yieldToken]; uint256 amountUnderlyingTokens = convertYieldTokensToUnderlying(yieldToken, amount); uint256 updatedActiveBalance = operation(yieldTokenParams.activeBalance, amount); uint256 updatedExpectedValue = operation(yieldTokenParams.expectedValue, amountUnderlyingTokens); _yieldTokens[yieldToken].activeBalance = updatedActiveBalance; _yieldTokens[yieldToken].expectedValue = updatedExpectedValue; } /// @dev Gets the amount of loss that `yieldToken` has incurred measured in basis points. When the expected /// underlying value is less than the actual value, this will return zero. /// /// @param yieldToken The address of the yield token. /// /// @return The loss in basis points. function _loss(address yieldToken) internal view returns (uint256) { YieldTokenParams memory yieldTokenParams = _yieldTokens[yieldToken]; uint256 amountUnderlyingTokens = convertYieldTokensToUnderlying(yieldToken, yieldTokenParams.activeBalance); uint256 expectedUnderlyingValue = yieldTokenParams.expectedValue; return expectedUnderlyingValue > amountUnderlyingTokens ? ((expectedUnderlyingValue - amountUnderlyingTokens) * BPS) / expectedUnderlyingValue : 0; } /// @dev Distributes `amount` credit to all depositors of `yieldToken`. /// /// @param yieldToken The address of the yield token to distribute credit for. /// @param amount The amount of credit to distribute in debt tokens. function _distributeCredit(address yieldToken, uint256 amount) internal { YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; uint256 pendingCredit = yieldTokenParams.pendingCredit; uint256 distributedCredit = yieldTokenParams.distributedCredit; uint256 unlockedCredit = _calculateUnlockedCredit(yieldToken); uint256 lockedCredit = pendingCredit - (distributedCredit + unlockedCredit); // Distribute any unlocked credit before overriding it. if (unlockedCredit > 0) { yieldTokenParams.accruedWeight += unlockedCredit * FIXED_POINT_SCALAR / yieldTokenParams.totalShares; } yieldTokenParams.pendingCredit = amount + lockedCredit; yieldTokenParams.distributedCredit = 0; yieldTokenParams.lastDistributionBlock = block.number; } /// @dev Distributes unlocked credit for all of the yield tokens that have been deposited into the account owned /// by `owner`. /// /// @param owner The address of the account owner. function _distributeUnlockedCreditDeposited(address owner) internal { Sets.AddressSet storage depositedTokens = _accounts[owner].depositedTokens; for (uint256 i = 0; i < depositedTokens.values.length; ++i) { _distributeUnlockedCredit(depositedTokens.values[i]); } } /// @dev Distributes unlocked credit of `yieldToken` to all depositors. /// /// @param yieldToken The address of the yield token to distribute unlocked credit for. function _distributeUnlockedCredit(address yieldToken) internal { YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; uint256 unlockedCredit = _calculateUnlockedCredit(yieldToken); if (unlockedCredit == 0) { return; } yieldTokenParams.accruedWeight += unlockedCredit * FIXED_POINT_SCALAR / yieldTokenParams.totalShares; yieldTokenParams.distributedCredit += unlockedCredit; } /// @dev Wraps `amount` of an underlying token into its `yieldToken`. /// /// @param yieldToken The address of the yield token to wrap the underlying tokens into. /// @param amount The amount of the underlying token to wrap. /// @param minimumAmountOut The minimum amount of yield tokens that are expected to be received from the operation. /// /// @return The amount of yield tokens that resulted from the operation. function _wrap( address yieldToken, uint256 amount, uint256 minimumAmountOut ) internal returns (uint256) { YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; ITokenAdapter adapter = ITokenAdapter(yieldTokenParams.adapter); address underlyingToken = yieldTokenParams.underlyingToken; TokenUtils.safeTransferFrom(underlyingToken, msg.sender, address(this), amount); uint256 wrappedShares = adapter.wrap(amount, address(this)); if (wrappedShares < minimumAmountOut) { revert SlippageExceeded(wrappedShares, minimumAmountOut); } return wrappedShares; } /// @dev Unwraps `amount` of `yieldToken` into its underlying token. /// /// @param yieldToken The address of the yield token to unwrap. /// @param amount The amount of the underlying token to wrap. /// @param recipient The recipient of the tokens after they are unwrapped. /// @param minimumAmountOut The minimum amount of underlying tokens that are expected to be received from the /// operation. /// /// @return The amount of underlying tokens that resulted from the operation. function _unwrap( address yieldToken, uint256 amount, address recipient, uint256 minimumAmountOut ) internal returns (uint256) { ITokenAdapter adapter = ITokenAdapter(_yieldTokens[yieldToken].adapter); uint256 amountUnwrapped = adapter.unwrap(amount, recipient); if (amountUnwrapped < minimumAmountOut) { revert SlippageExceeded(amountUnwrapped, minimumAmountOut); } return amountUnwrapped; } /// @dev Synchronizes the state for all of the tokens deposited in the account owned by `owner`. /// /// @param owner The address of the account owner. function _poke(address owner) internal { Sets.AddressSet storage depositedTokens = _accounts[owner].depositedTokens; for (uint256 i = 0; i < depositedTokens.values.length; ++i) { _poke(owner, depositedTokens.values[i]); } } /// @dev Synchronizes the state of `yieldToken` for the account owned by `owner`. /// /// @param owner The address of the account owner. /// @param yieldToken The address of the yield token to synchronize the state for. function _poke(address owner, address yieldToken) internal { Account storage account = _accounts[owner]; uint256 currentAccruedWeight = _yieldTokens[yieldToken].accruedWeight; uint256 lastAccruedWeight = account.lastAccruedWeights[yieldToken]; if (currentAccruedWeight == lastAccruedWeight) { return; } uint256 balance = account.balances[yieldToken]; uint256 unrealizedCredit = (currentAccruedWeight - lastAccruedWeight) * balance / FIXED_POINT_SCALAR; account.debt -= SafeCast.toInt256(unrealizedCredit); account.lastAccruedWeights[yieldToken] = currentAccruedWeight; } /// @dev Increases the debt by `amount` for the account owned by `owner`. /// /// @param owner The address of the account owner. /// @param amount The amount to increase the debt by. function _updateDebt(address owner, int256 amount) internal { Account storage account = _accounts[owner]; account.debt += amount; } /// @dev Set the mint allowance for `spender` to `amount` for the account owned by `owner`. /// /// @param owner The address of the account owner. /// @param spender The address of the spender. /// @param amount The amount of debt tokens to set the mint allowance to. function _approveMint(address owner, address spender, uint256 amount) internal { Account storage account = _accounts[owner]; account.mintAllowances[spender] = amount; emit ApproveMint(owner, spender, amount); } /// @dev Decrease the mint allowance for `spender` by `amount` for the account owned by `owner`. /// /// @param owner The address of the account owner. /// @param spender The address of the spender. /// @param amount The amount of debt tokens to decrease the mint allowance by. function _decreaseMintAllowance(address owner, address spender, uint256 amount) internal { Account storage account = _accounts[owner]; account.mintAllowances[spender] -= amount; } /// @dev Set the withdraw allowance of `yieldToken` for `spender` to `shares` for the account owned by `owner`. /// /// @param owner The address of the account owner. /// @param spender The address of the spender. /// @param yieldToken The address of the yield token to set the withdraw allowance for. /// @param shares The amount of shares to set the withdraw allowance to. function _approveWithdraw(address owner, address spender, address yieldToken, uint256 shares) internal { Account storage account = _accounts[owner]; account.withdrawAllowances[spender][yieldToken] = shares; emit ApproveWithdraw(owner, spender, yieldToken, shares); } /// @dev Decrease the withdraw allowance of `yieldToken` for `spender` by `amount` for the account owned by `owner`. /// /// @param owner The address of the account owner. /// @param spender The address of the spender. /// @param yieldToken The address of the yield token to decrease the withdraw allowance for. /// @param amount The amount of shares to decrease the withdraw allowance by. function _decreaseWithdrawAllowance(address owner, address spender, address yieldToken, uint256 amount) internal { Account storage account = _accounts[owner]; account.withdrawAllowances[spender][yieldToken] -= amount; } /// @dev Checks that the account owned by `owner` is properly collateralized. /// /// @dev If the account is undercollateralized then this will revert with an {Undercollateralized} error. /// /// @param owner The address of the account owner. function _validate(address owner) internal view { int256 debt = _accounts[owner].debt; if (debt <= 0) { return; } uint256 collateralization = totalValue(owner) * FIXED_POINT_SCALAR / uint256(debt); if (collateralization < minimumCollateralization) { revert Undercollateralized(); } } /// @dev Gets the total value of the deposit collateral measured in debt tokens of the account owned by `owner`. /// /// @param owner The address of the account owner. /// /// @return The total value. function totalValue(address owner) public view returns (uint256) { uint256 totalValue = 0; Sets.AddressSet storage depositedTokens = _accounts[owner].depositedTokens; for (uint256 i = 0; i < depositedTokens.values.length; ++i) { address yieldToken = depositedTokens.values[i]; address underlyingToken = _yieldTokens[yieldToken].underlyingToken; uint256 shares = _accounts[owner].balances[yieldToken]; uint256 amountUnderlyingTokens = convertSharesToUnderlyingTokens(yieldToken, shares); totalValue += normalizeUnderlyingTokensToDebt(underlyingToken, amountUnderlyingTokens); } return totalValue; } /// @dev Issues shares of `yieldToken` for `amount` of its underlying token to `recipient`. /// /// IMPORTANT: `amount` must never be 0. /// /// @param recipient The address of the recipient. /// @param yieldToken The address of the yield token. /// @param amount The amount of the underlying token. /// /// @return The amount of shares issued to `recipient`. function _issueSharesForAmount( address recipient, address yieldToken, uint256 amount ) internal returns (uint256) { uint256 shares = convertYieldTokensToShares(yieldToken, amount); if (_accounts[recipient].balances[yieldToken] == 0) { _accounts[recipient].depositedTokens.add(yieldToken); } _accounts[recipient].balances[yieldToken] += shares; _yieldTokens[yieldToken].totalShares += shares; return shares; } /// @dev Burns `share` shares of `yieldToken` from the account owned by `owner`. /// /// @param owner The address of the owner. /// @param yieldToken The address of the yield token. /// @param shares The amount of shares to burn. function _burnShares(address owner, address yieldToken, uint256 shares) internal { Account storage account = _accounts[owner]; account.balances[yieldToken] -= shares; _yieldTokens[yieldToken].totalShares -= shares; if (account.balances[yieldToken] == 0) { account.depositedTokens.remove(yieldToken); } } /// @dev Gets the amount of debt that the account owned by `owner` will have after an update occurs. /// /// @param owner The address of the account owner. /// /// @return The amount of debt that the account owned by `owner` will have after an update. function _calculateUnrealizedDebt(address owner) internal view returns (int256) { int256 debt = _accounts[owner].debt; Sets.AddressSet storage depositedTokens = _accounts[owner].depositedTokens; for (uint256 i = 0; i < depositedTokens.values.length; ++i) { address yieldToken = depositedTokens.values[i]; uint256 currentAccruedWeight = _yieldTokens[yieldToken].accruedWeight; uint256 lastAccruedWeight = _accounts[owner].lastAccruedWeights[yieldToken]; uint256 unlockedCredit = _calculateUnlockedCredit(yieldToken); currentAccruedWeight += unlockedCredit > 0 ? unlockedCredit * FIXED_POINT_SCALAR / _yieldTokens[yieldToken].totalShares : 0; if (currentAccruedWeight == lastAccruedWeight) { continue; } uint256 balance = _accounts[owner].balances[yieldToken]; uint256 unrealizedCredit = ((currentAccruedWeight - lastAccruedWeight) * balance) / FIXED_POINT_SCALAR; debt -= SafeCast.toInt256(unrealizedCredit); } return debt; } /// @dev Gets the virtual active balance of `yieldToken`. /// /// @dev The virtual active balance is the active balance minus any harvestable tokens which have yet to be realized. /// /// @param yieldToken The address of the yield token to get the virtual active balance of. /// /// @return The virtual active balance. function _calculateUnrealizedActiveBalance(address yieldToken) internal view returns (uint256) { YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; uint256 activeBalance = yieldTokenParams.activeBalance; if (activeBalance == 0) { return activeBalance; } uint256 currentValue = convertYieldTokensToUnderlying(yieldToken, activeBalance); uint256 expectedValue = yieldTokenParams.expectedValue; if (currentValue <= expectedValue) { return activeBalance; } uint256 harvestable = convertUnderlyingTokensToYield(yieldToken, currentValue - expectedValue); if (harvestable == 0) { return activeBalance; } return activeBalance - harvestable; } /// @dev Calculates the amount of unlocked credit for `yieldToken` that is available for distribution. /// /// @param yieldToken The address of the yield token. /// /// @return The amount of unlocked credit available. function _calculateUnlockedCredit(address yieldToken) internal view returns (uint256) { YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; uint256 pendingCredit = yieldTokenParams.pendingCredit; if (pendingCredit == 0) { return 0; } uint256 creditUnlockRate = yieldTokenParams.creditUnlockRate; uint256 distributedCredit = yieldTokenParams.distributedCredit; uint256 lastDistributionBlock = yieldTokenParams.lastDistributionBlock; uint256 percentUnlocked = (block.number - lastDistributionBlock) * creditUnlockRate; return percentUnlocked < FIXED_POINT_SCALAR ? (pendingCredit * percentUnlocked / FIXED_POINT_SCALAR) - distributedCredit : pendingCredit - distributedCredit; } /// @dev Gets the amount of shares that `amount` of `yieldToken` is exchangeable for. /// /// @param yieldToken The address of the yield token. /// @param amount The amount of yield tokens. /// /// @return The number of shares. function convertYieldTokensToShares(address yieldToken, uint256 amount) public view returns (uint256) { if (_yieldTokens[yieldToken].totalShares == 0) { return amount; } return amount * _yieldTokens[yieldToken].totalShares / _calculateUnrealizedActiveBalance(yieldToken); } /// @dev Gets the amount of yield tokens that `shares` shares of `yieldToken` is exchangeable for. /// /// @param yieldToken The address of the yield token. /// @param shares The amount of shares. /// /// @return The amount of yield tokens. function convertSharesToYieldTokens(address yieldToken, uint256 shares) public view returns (uint256) { uint256 totalShares = _yieldTokens[yieldToken].totalShares; if (totalShares == 0) { return shares; } return (shares * _calculateUnrealizedActiveBalance(yieldToken)) / totalShares; } /// @dev Gets the amount of underlying tokens that `shares` shares of `yieldToken` is exchangeable for. /// /// @param yieldToken The address of the yield token. /// @param shares The amount of shares. /// /// @return The amount of underlying tokens. function convertSharesToUnderlyingTokens(address yieldToken, uint256 shares) public view returns (uint256) { uint256 amountYieldTokens = convertSharesToYieldTokens(yieldToken, shares); return convertYieldTokensToUnderlying(yieldToken, amountYieldTokens); } /// @dev Gets the amount of an underlying token that `amount` of `yieldToken` is exchangeable for. /// /// @param yieldToken The address of the yield token. /// @param amount The amount of yield tokens. /// /// @return The amount of underlying tokens. function convertYieldTokensToUnderlying(address yieldToken, uint256 amount) public view returns (uint256) { YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; ITokenAdapter adapter = ITokenAdapter(yieldTokenParams.adapter); return amount * adapter.price() / 10**yieldTokenParams.decimals; } /// @dev Gets the amount of `yieldToken` that `amount` of its underlying token is exchangeable for. /// /// @param yieldToken The address of the yield token. /// @param amount The amount of underlying tokens. /// /// @return The amount of yield tokens. function convertUnderlyingTokensToYield(address yieldToken, uint256 amount) public view returns (uint256) { YieldTokenParams storage yieldTokenParams = _yieldTokens[yieldToken]; ITokenAdapter adapter = ITokenAdapter(yieldTokenParams.adapter); return amount * 10**yieldTokenParams.decimals / adapter.price(); } /// @dev Gets the amount of shares of `yieldToken` that `amount` of its underlying token is exchangeable for. /// /// @param yieldToken The address of the yield token. /// @param amount The amount of underlying tokens. /// /// @return The amount of shares. function convertUnderlyingTokensToShares(address yieldToken, uint256 amount) public view returns (uint256) { uint256 amountYieldTokens = convertUnderlyingTokensToYield(yieldToken, amount); return convertYieldTokensToShares(yieldToken, amountYieldTokens); } /// @dev Normalize `amount` of `underlyingToken` to a value which is comparable to units of the debt token. /// /// @param underlyingToken The address of the underlying token. /// @param amount The amount of the debt token. /// /// @return The normalized amount. function normalizeUnderlyingTokensToDebt(address underlyingToken, uint256 amount) public view returns (uint256) { return amount * _underlyingTokens[underlyingToken].conversionFactor; } /// @dev Normalize `amount` of the debt token to a value which is comparable to units of `underlyingToken`. /// /// @dev This operation will result in truncation of some of the least significant digits of `amount`. This /// truncation amount will be the least significant N digits where N is the difference in decimals between /// the debt token and the underlying token. /// /// @param underlyingToken The address of the underlying token. /// @param amount The amount of the debt token. /// /// @return The normalized amount. function normalizeDebtTokensToUnderlying(address underlyingToken, uint256 amount) public view returns (uint256) { return amount / _underlyingTokens[underlyingToken].conversionFactor; } /// @dev Checks the whitelist for msg.sender. /// /// Reverts if msg.sender is not in the whitelist. function _onlyWhitelisted() internal view { // Check if the message sender is an EOA. In the future, this potentially may break. It is important that functions // which rely on the whitelist not be explicitly vulnerable in the situation where this no longer holds true. if (tx.origin == msg.sender) { return; } // Only check the whitelist for calls from contracts. if (!IWhitelist(whitelist).isWhitelisted(msg.sender)) { revert Unauthorized(); } } /// @dev Checks an expression and reverts with an {IllegalArgument} error if the expression is {false}. /// /// @param expression The expression to check. function _checkArgument(bool expression) internal pure { if (!expression) { revert IllegalArgument(); } } /// @dev Checks an expression and reverts with an {IllegalState} error if the expression is {false}. /// /// @param expression The expression to check. function _checkState(bool expression) internal pure { if (!expression) { revert IllegalState(); } } /// @dev Adds two unsigned 256 bit integers together and returns the result. /// /// @dev This operation is checked and will fail if the result overflows. /// /// @param x The first operand. /// @param y The second operand. /// /// @return z The result. function _uadd(uint256 x, uint256 y) internal pure returns (uint256 z) { z = x + y; } /// @dev Subtracts two unsigned 256 bit integers together and returns the result. /// /// @dev This operation is checked and will fail if the result overflows. /// /// @param x The first operand. /// @param y The second operand. /// /// @return z the result. function _usub(uint256 x, uint256 y) internal pure returns (uint256 z) { z = x - y; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ``` * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a * constructor. * * Emits an {Initialized} event. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: setting the version to 255 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized < type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /** * @dev Internal function that returns the initialized version. Returns `_initialized` */ function _getInitializedVersion() internal view returns (uint8) { return _initialized; } /** * @dev Internal function that returns the initialized version. Returns `_initializing` */ function _isInitializing() internal view returns (bool) { return _initializing; } }
pragma solidity ^0.8.13; /// @notice An error used to indicate that an action could not be completed because either the `msg.sender` or /// `msg.origin` is not authorized. error Unauthorized(); /// @notice An error used to indicate that an action could not be completed because the contract either already existed /// or entered an illegal condition which is not recoverable from. error IllegalState(); /// @notice An error used to indicate that an action could not be completed because of an illegal argument was passed /// to the function. error IllegalArgument();
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity 0.8.13; /// @title Multicall /// @author Uniswap Labs /// /// @notice Enables calling multiple methods in a single call to the contract abstract contract Multicall { error MulticallFailed(bytes data, bytes result); function multicall( bytes[] calldata data ) external payable returns (bytes[] memory results) { results = new bytes[](data.length); for (uint256 i = 0; i < data.length; ++i) { (bool success, bytes memory result) = address(this).delegatecall(data[i]); if (!success) { revert MulticallFailed(data[i], result); } results[i] = result; } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.13; /// @title Mutex /// @author Alchemix Finance /// /// @notice Provides a mutual exclusion lock for implementing contracts. abstract contract Mutex { /// @notice An error which is thrown when a lock is attempted to be claimed before it has been freed. error LockAlreadyClaimed(); /// @notice The lock state. Non-zero values indicate the lock has been claimed. uint256 private _lockState; /// @dev A modifier which acquires the mutex. modifier lock() { _claimLock(); _; _freeLock(); } /// @dev Gets if the mutex is locked. /// /// @return if the mutex is locked. function _isLocked() internal returns (bool) { return _lockState == 1; } /// @dev Claims the lock. If the lock is already claimed, then this will revert. function _claimLock() internal { // Check that the lock has not been claimed yet. if (_lockState != 0) { revert LockAlreadyClaimed(); } // Claim the lock. _lockState = 1; } /// @dev Frees the lock. function _freeLock() internal { _lockState = 0; } }
pragma solidity >=0.5.0; import "./alchemist/IAlchemistV2Actions.sol"; import "./alchemist/IAlchemistV2AdminActions.sol"; import "./alchemist/IAlchemistV2Errors.sol"; import "./alchemist/IAlchemistV2Immutables.sol"; import "./alchemist/IAlchemistV2Events.sol"; import "./alchemist/IAlchemistV2State.sol"; /// @title IAlchemistV2 /// @author Alchemix Finance interface IAlchemistV2 is IAlchemistV2Actions, IAlchemistV2AdminActions, IAlchemistV2Errors, IAlchemistV2Immutables, IAlchemistV2Events, IAlchemistV2State { }
pragma solidity >=0.5.0; /// @title IERC20TokenReceiver /// @author Alchemix Finance interface IERC20TokenReceiver { /// @notice Informs implementors of this interface that an ERC20 token has been transferred. /// /// @param token The token that was transferred. /// @param value The amount of the token that was transferred. function onERC20Received(address token, uint256 value) external; }
pragma solidity >=0.5.0; /// @title ITokenAdapter /// @author Alchemix Finance interface ITokenAdapter { /// @notice Gets the current version. /// /// @return The version. function version() external view returns (string memory); /// @notice Gets the address of the yield token that this adapter supports. /// /// @return The address of the yield token. function token() external view returns (address); /// @notice Gets the address of the underlying token that the yield token wraps. /// /// @return The address of the underlying token. function underlyingToken() external view returns (address); /// @notice Gets the number of underlying tokens that a single whole yield token is redeemable /// for. /// /// @return The price. function price() external view returns (uint256); /// @notice Wraps `amount` underlying tokens into the yield token. /// /// @param amount The amount of the underlying token to wrap. /// @param recipient The address which will receive the yield tokens. /// /// @return amountYieldTokens The amount of yield tokens minted to `recipient`. function wrap(uint256 amount, address recipient) external returns (uint256 amountYieldTokens); /// @notice Unwraps `amount` yield tokens into the underlying token. /// /// @param amount The amount of yield-tokens to redeem. /// @param recipient The recipient of the resulting underlying-tokens. /// /// @return amountUnderlyingTokens The amount of underlying tokens unwrapped to `recipient`. function unwrap(uint256 amount, address recipient) external returns (uint256 amountUnderlyingTokens); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.5.0; import {IERC20} from "../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; /// @title IAlchemicToken /// @author Alchemix Finance interface IAlchemicToken is IERC20 { /// @notice Gets the total amount of minted tokens for an account. /// /// @param account The address of the account. /// /// @return The total minted. function hasMinted(address account) external view returns (uint256); /// @notice Lowers the number of tokens which the `msg.sender` has minted. /// /// This reverts if the `msg.sender` is not whitelisted. /// /// @param amount The amount to lower the minted amount by. function lowerHasMinted(uint256 amount) external; /// @notice Sets the mint allowance for a given account' /// /// This reverts if the `msg.sender` is not admin /// /// @param toSetCeiling The account whos allowance to update /// @param ceiling The amount of tokens allowed to mint function setCeiling(address toSetCeiling, uint256 ceiling) external; /// @notice Updates the state of an address in the whitelist map /// /// This reverts if msg.sender is not admin /// /// @param toWhitelist the address whos state is being updated /// @param state the boolean state of the whitelist function setWhitelist(address toWhitelist, bool state) external; function mint(address recipient, uint256 amount) external; function burn(uint256 amount) external; }
pragma solidity ^0.8.13; /// @title Whitelist /// @author Alchemix Finance interface IWhitelist { /// @dev Emitted when a contract is added to the whitelist. /// /// @param account The account that was added to the whitelist. event AccountAdded(address account); /// @dev Emitted when a contract is removed from the whitelist. /// /// @param account The account that was removed from the whitelist. event AccountRemoved(address account); /// @dev Emitted when the whitelist is deactivated. event WhitelistDisabled(); /// @dev Returns the list of addresses that are whitelisted for the given contract address. /// /// @return addresses The addresses that are whitelisted to interact with the given contract. function getAddresses() external view returns (address[] memory addresses); /// @dev Returns the disabled status of a given whitelist. /// /// @return disabled A flag denoting if the given whitelist is disabled. function disabled() external view returns (bool); /// @dev Adds an contract to the whitelist. /// /// @param caller The address to add to the whitelist. function add(address caller) external; /// @dev Adds a contract to the whitelist. /// /// @param caller The address to remove from the whitelist. function remove(address caller) external; /// @dev Disables the whitelist of the target whitelisted contract. /// /// This can only occur once. Once the whitelist is disabled, then it cannot be reenabled. function disable() external; /// @dev Checks that the `msg.sender` is whitelisted when it is not an EOA. /// /// @param account The account to check. /// /// @return whitelisted A flag denoting if the given account is whitelisted. function isWhitelisted(address account) external view returns (bool); }
pragma solidity ^0.8.13; interface IRewardCollector { /// @notice Gets the current version. /// /// @return The version. function version() external view returns (string memory); /// @notice Gets the current reward token. /// /// @return The reward token. function rewardToken() external view returns (address); /// @notice Gets the current swap router. /// /// @return The swap router address. function swapRouter() external view returns (address); /// @notice Gets the current debt token. /// /// @return The debt token function debtToken() external view returns (address); /// @notice Claims rewards tokens, swaps for debt tokens. /// /// @param token The yield token to claim rewards for. /// @param minimumAmountOut The minimum returns to accept. /// /// @return claimed The amount of reward tokens claimed. function claimAndDonateRewards(address token, uint256 minimumAmountOut) external returns (uint256 claimed); /// @notice Gets expected exchange for OP to debt token. function getExpectedExchange(address yieldToken) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.12; interface IVesperRewards { function claimReward(address) external; function claimable(address) external view returns (address[] memory, uint256[] memory); function rewardTokens(uint256) external view returns (address); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.5.0; import {IERC20} from "../../../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; interface IVesperPool is IERC20 { function deposit() external payable; function deposit(uint256 _share) external; function governor() external returns (address); function keepers() external returns (address); function multiTransfer(address[] memory _recipients, uint256[] memory _amounts) external returns (bool); function excessDebt(address _strategy) external view returns (uint256); function permit( address, address, uint256, uint256, uint8, bytes32, bytes32 ) external; function reportEarning( uint256 _profit, uint256 _loss, uint256 _payback ) external; function resetApproval() external; function sweepERC20(address _fromToken) external; function withdraw(uint256 _amount) external; function withdrawETH(uint256 _amount) external; function whitelistedWithdraw(uint256 _amount) external; function feeCollector() external view returns (address); function pricePerShare() external view returns (uint256); function token() external view returns (address); function tokensHere() external view returns (uint256); function totalDebtOf(address _strategy) external view returns (uint256); function totalValue() external view returns (uint256); function withdrawFee() external view returns (uint256); function poolRewards() external view returns (address); function getStrategies() external view returns (address[] memory); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; import {IllegalArgument} from "../base/Errors.sol"; /// @title Safe casting methods /// @notice Contains methods for safely casting between types library SafeCast { /// @notice Cast a uint256 to a int256, revert on overflow /// @param y The uint256 to be casted /// @return z The casted integer, now type int256 function toInt256(uint256 y) internal pure returns (int256 z) { if (y >= 2**255) { revert IllegalArgument(); } z = int256(y); } /// @notice Cast a int256 to a uint256, revert on underflow /// @param y The int256 to be casted /// @return z The casted integer, now type uint256 function toUint256(int256 y) internal pure returns (uint256 z) { if (y < 0) { revert IllegalArgument(); } z = uint256(y); } }
pragma solidity ^0.8.13; /// @title Sets /// @author Alchemix Finance library Sets { using Sets for AddressSet; /// @notice A data structure holding an array of values with an index mapping for O(1) lookup. struct AddressSet { address[] values; mapping(address => uint256) indexes; } /// @dev Add a value to a Set /// /// @param self The Set. /// @param value The value to add. /// /// @return Whether the operation was successful (unsuccessful if the value is already contained in the Set) function add(AddressSet storage self, address value) internal returns (bool) { if (self.contains(value)) { return false; } self.values.push(value); self.indexes[value] = self.values.length; return true; } /// @dev Remove a value from a Set /// /// @param self The Set. /// @param value The value to remove. /// /// @return Whether the operation was successful (unsuccessful if the value was not contained in the Set) function remove(AddressSet storage self, address value) internal returns (bool) { uint256 index = self.indexes[value]; if (index == 0) { return false; } // Normalize the index since we know that the element is in the set. index--; uint256 lastIndex = self.values.length - 1; if (index != lastIndex) { address lastValue = self.values[lastIndex]; self.values[index] = lastValue; self.indexes[lastValue] = index + 1; } self.values.pop(); delete self.indexes[value]; return true; } /// @dev Returns true if the value exists in the Set /// /// @param self The Set. /// @param value The value to check. /// /// @return True if the value is contained in the Set, False if it is not. function contains(AddressSet storage self, address value) internal view returns (bool) { return self.indexes[value] != 0; } }
pragma solidity ^0.8.13; import "../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import "../../lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "../interfaces/IERC20Burnable.sol"; import "../interfaces/IERC20Mintable.sol"; /// @title TokenUtils /// @author Alchemix Finance library TokenUtils { /// @notice An error used to indicate that a call to an ERC20 contract failed. /// /// @param target The target address. /// @param success If the call to the token was a success. /// @param data The resulting data from the call. This is error data when the call was not a success. Otherwise, /// this is malformed data when the call was a success. error ERC20CallFailed(address target, bool success, bytes data); /// @dev A safe function to get the decimals of an ERC20 token. /// /// @dev Reverts with a {CallFailed} error if execution of the query fails or returns an unexpected value. /// /// @param token The target token. /// /// @return The amount of decimals of the token. function expectDecimals(address token) internal view returns (uint8) { (bool success, bytes memory data) = token.staticcall( abi.encodeWithSelector(IERC20Metadata.decimals.selector) ); if (token.code.length == 0 || !success || data.length < 32) { revert ERC20CallFailed(token, success, data); } return abi.decode(data, (uint8)); } /// @dev Gets the balance of tokens held by an account. /// /// @dev Reverts with a {CallFailed} error if execution of the query fails or returns an unexpected value. /// /// @param token The token to check the balance of. /// @param account The address of the token holder. /// /// @return The balance of the tokens held by an account. function safeBalanceOf(address token, address account) internal view returns (uint256) { (bool success, bytes memory data) = token.staticcall( abi.encodeWithSelector(IERC20.balanceOf.selector, account) ); if (token.code.length == 0 || !success || data.length < 32) { revert ERC20CallFailed(token, success, data); } return abi.decode(data, (uint256)); } /// @dev Transfers tokens to another address. /// /// @dev Reverts with a {CallFailed} error if execution of the transfer failed or returns an unexpected value. /// /// @param token The token to transfer. /// @param recipient The address of the recipient. /// @param amount The amount of tokens to transfer. function safeTransfer(address token, address recipient, uint256 amount) internal { (bool success, bytes memory data) = token.call( abi.encodeWithSelector(IERC20.transfer.selector, recipient, amount) ); if (token.code.length == 0 || !success || (data.length != 0 && !abi.decode(data, (bool)))) { revert ERC20CallFailed(token, success, data); } } /// @dev Approves tokens for the smart contract. /// /// @dev Reverts with a {CallFailed} error if execution of the approval fails or returns an unexpected value. /// /// @param token The token to approve. /// @param spender The contract to spend the tokens. /// @param value The amount of tokens to approve. function safeApprove(address token, address spender, uint256 value) internal { (bool success, bytes memory data) = token.call( abi.encodeWithSelector(IERC20.approve.selector, spender, value) ); if (token.code.length == 0 || !success || (data.length != 0 && !abi.decode(data, (bool)))) { revert ERC20CallFailed(token, success, data); } } /// @dev Transfer tokens from one address to another address. /// /// @dev Reverts with a {CallFailed} error if execution of the transfer fails or returns an unexpected value. /// /// @param token The token to transfer. /// @param owner The address of the owner. /// @param recipient The address of the recipient. /// @param amount The amount of tokens to transfer. function safeTransferFrom(address token, address owner, address recipient, uint256 amount) internal { (bool success, bytes memory data) = token.call( abi.encodeWithSelector(IERC20.transferFrom.selector, owner, recipient, amount) ); if (token.code.length == 0 || !success || (data.length != 0 && !abi.decode(data, (bool)))) { revert ERC20CallFailed(token, success, data); } } /// @dev Mints tokens to an address. /// /// @dev Reverts with a {CallFailed} error if execution of the mint fails or returns an unexpected value. /// /// @param token The token to mint. /// @param recipient The address of the recipient. /// @param amount The amount of tokens to mint. function safeMint(address token, address recipient, uint256 amount) internal { (bool success, bytes memory data) = token.call( abi.encodeWithSelector(IERC20Mintable.mint.selector, recipient, amount) ); if (token.code.length == 0 || !success || (data.length != 0 && !abi.decode(data, (bool)))) { revert ERC20CallFailed(token, success, data); } } /// @dev Burns tokens. /// /// Reverts with a `CallFailed` error if execution of the burn fails or returns an unexpected value. /// /// @param token The token to burn. /// @param amount The amount of tokens to burn. function safeBurn(address token, uint256 amount) internal { (bool success, bytes memory data) = token.call( abi.encodeWithSelector(IERC20Burnable.burn.selector, amount) ); if (token.code.length == 0 || !success || (data.length != 0 && !abi.decode(data, (bool)))) { revert ERC20CallFailed(token, success, data); } } /// @dev Burns tokens from its total supply. /// /// @dev Reverts with a {CallFailed} error if execution of the burn fails or returns an unexpected value. /// /// @param token The token to burn. /// @param owner The owner of the tokens. /// @param amount The amount of tokens to burn. function safeBurnFrom(address token, address owner, uint256 amount) internal { (bool success, bytes memory data) = token.call( abi.encodeWithSelector(IERC20Burnable.burnFrom.selector, owner, amount) ); if (token.code.length == 0 || !success || (data.length != 0 && !abi.decode(data, (bool)))) { revert ERC20CallFailed(token, success, data); } } }
pragma solidity ^0.8.13; import {IllegalArgument} from "../base/Errors.sol"; /// @title Functions /// @author Alchemix Finance library Limiters { using Limiters for LinearGrowthLimiter; /// @dev A maximum cooldown to avoid malicious governance bricking the contract. /// @dev 1 day @ 12 sec / block uint256 constant public MAX_COOLDOWN_BLOCKS = 1 days / 12 seconds; /// @dev The scalar used to convert integral types to fixed point numbers. uint256 constant public FIXED_POINT_SCALAR = 1e18; /// @dev The configuration and state of a linear growth function (LGF). struct LinearGrowthLimiter { uint256 maximum; /// The maximum limit of the function. uint256 rate; /// The rate at which the function increases back to its maximum. uint256 lastValue; /// The most recently saved value of the function. uint256 lastBlock; /// The block that `lastValue` was recorded. uint256 minLimit; /// A minimum limit to avoid malicious governance bricking the contract } /// @dev Instantiates a new linear growth function. /// /// @param maximum The maximum value for the LGF. /// @param blocks The number of blocks that determines the rate of the LGF. /// @param _minLimit The new minimum limit of the LGF. /// /// @return The LGF struct. function createLinearGrowthLimiter(uint256 maximum, uint256 blocks, uint256 _minLimit) internal view returns (LinearGrowthLimiter memory) { if (blocks > MAX_COOLDOWN_BLOCKS) { revert IllegalArgument(); } if (maximum < _minLimit) { revert IllegalArgument(); } return LinearGrowthLimiter({ maximum: maximum, rate: maximum * FIXED_POINT_SCALAR / blocks, lastValue: maximum, lastBlock: block.number, minLimit: _minLimit }); } /// @dev Configure an LGF. /// /// @param self The LGF to configure. /// @param maximum The maximum value of the LFG. /// @param blocks The number of recovery blocks of the LGF. function configure(LinearGrowthLimiter storage self, uint256 maximum, uint256 blocks) internal { if (blocks > MAX_COOLDOWN_BLOCKS) { revert IllegalArgument(); } if (maximum < self.minLimit) { revert IllegalArgument(); } if (self.lastValue > maximum) { self.lastValue = maximum; } self.maximum = maximum; self.rate = maximum * FIXED_POINT_SCALAR / blocks; } /// @dev Updates the state of an LGF by updating `lastValue` and `lastBlock`. /// /// @param self the LGF to update. function update(LinearGrowthLimiter storage self) internal { self.lastValue = self.get(); self.lastBlock = block.number; } /// @dev Increase the value of the linear growth limiter. /// /// @param self The linear growth limiter. /// @param amount The amount to decrease `lastValue`. function increase(LinearGrowthLimiter storage self, uint256 amount) internal { uint256 value = self.get(); self.lastValue = value + amount; self.lastBlock = block.number; } /// @dev Decrease the value of the linear growth limiter. /// /// @param self The linear growth limiter. /// @param amount The amount to decrease `lastValue`. function decrease(LinearGrowthLimiter storage self, uint256 amount) internal { uint256 value = self.get(); self.lastValue = value - amount; self.lastBlock = block.number; } /// @dev Get the current value of the linear growth limiter. /// /// @return The current value. function get(LinearGrowthLimiter storage self) internal view returns (uint256) { uint256 elapsed = block.number - self.lastBlock; if (elapsed == 0) { return self.lastValue; } uint256 delta = elapsed * self.rate / FIXED_POINT_SCALAR; uint256 value = self.lastValue + delta; return value > self.maximum ? self.maximum : value; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @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, it is bubbled up by this * function (like regular Solidity function calls). * * 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. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @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`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage); } } }
pragma solidity >=0.5.0; /// @title IAlchemistV2Actions /// @author Alchemix Finance /// /// @notice Specifies user actions. interface IAlchemistV2Actions { /// @notice Approve `spender` to mint `amount` debt tokens. /// /// **_NOTE:_** This function is WHITELISTED. /// /// @param spender The address that will be approved to mint. /// @param amount The amount of tokens that `spender` will be allowed to mint. function approveMint(address spender, uint256 amount) external; /// @notice Approve `spender` to withdraw `amount` shares of `yieldToken`. /// /// @notice **_NOTE:_** This function is WHITELISTED. /// /// @param spender The address that will be approved to withdraw. /// @param yieldToken The address of the yield token that `spender` will be allowed to withdraw. /// @param shares The amount of shares that `spender` will be allowed to withdraw. function approveWithdraw( address spender, address yieldToken, uint256 shares ) external; /// @notice Synchronizes the state of the account owned by `owner`. /// /// @param owner The owner of the account to synchronize. function poke(address owner) external; /// @notice Deposit a yield token into a user's account. /// /// @notice An approval must be set for `yieldToken` which is greater than `amount`. /// /// @notice `yieldToken` must be registered or this call will revert with a {UnsupportedToken} error. /// @notice `yieldToken` must be enabled or this call will revert with a {TokenDisabled} error. /// @notice `yieldToken` underlying token must be enabled or this call will revert with a {TokenDisabled} error. /// @notice `recipient` must be non-zero or this call will revert with an {IllegalArgument} error. /// @notice `amount` must be greater than zero or the call will revert with an {IllegalArgument} error. /// /// @notice Emits a {Deposit} event. /// /// @notice **_NOTE:_** This function is WHITELISTED. /// /// @notice **_NOTE:_** When depositing, the `AlchemistV2` contract must have **allowance()** to spend funds on behalf of **msg.sender** for at least **amount** of the **yieldToken** being deposited. This can be done via the standard `ERC20.approve()` method. /// /// @notice **Example:** /// @notice ``` /// @notice address ydai = 0xdA816459F1AB5631232FE5e97a05BBBb94970c95; /// @notice uint256 amount = 50000; /// @notice IERC20(ydai).approve(alchemistAddress, amount); /// @notice AlchemistV2(alchemistAddress).deposit(ydai, amount, msg.sender); /// @notice ``` /// /// @param yieldToken The yield-token to deposit. /// @param amount The amount of yield tokens to deposit. /// @param recipient The owner of the account that will receive the resulting shares. /// /// @return sharesIssued The number of shares issued to `recipient`. function deposit( address yieldToken, uint256 amount, address recipient ) external returns (uint256 sharesIssued); /// @notice Deposit an underlying token into the account of `recipient` as `yieldToken`. /// /// @notice An approval must be set for the underlying token of `yieldToken` which is greater than `amount`. /// /// @notice `yieldToken` must be registered or this call will revert with a {UnsupportedToken} error. /// @notice `recipient` must be non-zero or this call will revert with an {IllegalArgument} error. /// @notice `amount` must be greater than zero or the call will revert with an {IllegalArgument} error. /// /// @notice Emits a {Deposit} event. /// /// @notice **_NOTE:_** This function is WHITELISTED. /// @notice **_NOTE:_** When depositing, the `AlchemistV2` contract must have **allowance()** to spend funds on behalf of **msg.sender** for at least **amount** of the **underlyingToken** being deposited. This can be done via the standard `ERC20.approve()` method. /// /// @notice **Example:** /// @notice ``` /// @notice address ydai = 0xdA816459F1AB5631232FE5e97a05BBBb94970c95; /// @notice uint256 amount = 50000; /// @notice AlchemistV2(alchemistAddress).depositUnderlying(ydai, amount, msg.sender, 1); /// @notice ``` /// /// @param yieldToken The address of the yield token to wrap the underlying tokens into. /// @param amount The amount of the underlying token to deposit. /// @param recipient The address of the recipient. /// @param minimumAmountOut The minimum amount of yield tokens that are expected to be deposited to `recipient`. /// /// @return sharesIssued The number of shares issued to `recipient`. function depositUnderlying( address yieldToken, uint256 amount, address recipient, uint256 minimumAmountOut ) external returns (uint256 sharesIssued); /// @notice Withdraw yield tokens to `recipient` by burning `share` shares. The number of yield tokens withdrawn to `recipient` will depend on the value of shares for that yield token at the time of the call. /// /// @notice `yieldToken` must be registered or this call will revert with a {UnsupportedToken} error. /// @notice `recipient` must be non-zero or this call will revert with an {IllegalArgument} error. /// /// @notice Emits a {Withdraw} event. /// /// @notice **_NOTE:_** This function is WHITELISTED. /// /// @notice **Example:** /// @notice ``` /// @notice address ydai = 0xdA816459F1AB5631232FE5e97a05BBBb94970c95; /// @notice uint256 pps = AlchemistV2(alchemistAddress).getYieldTokensPerShare(ydai); /// @notice uint256 amtYieldTokens = 5000; /// @notice AlchemistV2(alchemistAddress).withdraw(ydai, amtYieldTokens / pps, msg.sender); /// @notice ``` /// /// @param yieldToken The address of the yield token to withdraw. /// @param shares The number of shares to burn. /// @param recipient The address of the recipient. /// /// @return amountWithdrawn The number of yield tokens that were withdrawn to `recipient`. function withdraw( address yieldToken, uint256 shares, address recipient ) external returns (uint256 amountWithdrawn); /// @notice Withdraw yield tokens to `recipient` by burning `share` shares from the account of `owner` /// /// @notice `owner` must have an withdrawal allowance which is greater than `amount` for this call to succeed. /// /// @notice `yieldToken` must be registered or this call will revert with a {UnsupportedToken} error. /// @notice `recipient` must be non-zero or this call will revert with an {IllegalArgument} error. /// /// @notice Emits a {Withdraw} event. /// /// @notice **_NOTE:_** This function is WHITELISTED. /// /// @notice **Example:** /// @notice ``` /// @notice address ydai = 0xdA816459F1AB5631232FE5e97a05BBBb94970c95; /// @notice uint256 pps = AlchemistV2(alchemistAddress).getYieldTokensPerShare(ydai); /// @notice uint256 amtYieldTokens = 5000; /// @notice AlchemistV2(alchemistAddress).withdrawFrom(msg.sender, ydai, amtYieldTokens / pps, msg.sender); /// @notice ``` /// /// @param owner The address of the account owner to withdraw from. /// @param yieldToken The address of the yield token to withdraw. /// @param shares The number of shares to burn. /// @param recipient The address of the recipient. /// /// @return amountWithdrawn The number of yield tokens that were withdrawn to `recipient`. function withdrawFrom( address owner, address yieldToken, uint256 shares, address recipient ) external returns (uint256 amountWithdrawn); /// @notice Withdraw underlying tokens to `recipient` by burning `share` shares and unwrapping the yield tokens that the shares were redeemed for. /// /// @notice `yieldToken` must be registered or this call will revert with a {UnsupportedToken} error. /// @notice `recipient` must be non-zero or this call will revert with an {IllegalArgument} error. /// @notice The loss in expected value of `yieldToken` must be less than the maximum permitted by the system or this call will revert with a {LossExceeded} error. /// /// @notice Emits a {Withdraw} event. /// /// @notice **_NOTE:_** This function is WHITELISTED. /// @notice **_NOTE:_** The caller of `withdrawFrom()` must have **withdrawAllowance()** to withdraw funds on behalf of **owner** for at least the amount of `yieldTokens` that **shares** will be converted to. This can be done via the `approveWithdraw()` or `permitWithdraw()` methods. /// /// @notice **Example:** /// @notice ``` /// @notice address ydai = 0xdA816459F1AB5631232FE5e97a05BBBb94970c95; /// @notice uint256 pps = AlchemistV2(alchemistAddress).getUnderlyingTokensPerShare(ydai); /// @notice uint256 amountUnderlyingTokens = 5000; /// @notice AlchemistV2(alchemistAddress).withdrawUnderlying(ydai, amountUnderlyingTokens / pps, msg.sender, 1); /// @notice ``` /// /// @param yieldToken The address of the yield token to withdraw. /// @param shares The number of shares to burn. /// @param recipient The address of the recipient. /// @param minimumAmountOut The minimum amount of underlying tokens that are expected to be withdrawn to `recipient`. /// /// @return amountWithdrawn The number of underlying tokens that were withdrawn to `recipient`. function withdrawUnderlying( address yieldToken, uint256 shares, address recipient, uint256 minimumAmountOut ) external returns (uint256 amountWithdrawn); /// @notice Withdraw underlying tokens to `recipient` by burning `share` shares from the account of `owner` and unwrapping the yield tokens that the shares were redeemed for. /// /// @notice `yieldToken` must be registered or this call will revert with a {UnsupportedToken} error. /// @notice `recipient` must be non-zero or this call will revert with an {IllegalArgument} error. /// @notice The loss in expected value of `yieldToken` must be less than the maximum permitted by the system or this call will revert with a {LossExceeded} error. /// /// @notice Emits a {Withdraw} event. /// /// @notice **_NOTE:_** This function is WHITELISTED. /// @notice **_NOTE:_** The caller of `withdrawFrom()` must have **withdrawAllowance()** to withdraw funds on behalf of **owner** for at least the amount of `yieldTokens` that **shares** will be converted to. This can be done via the `approveWithdraw()` or `permitWithdraw()` methods. /// /// @notice **Example:** /// @notice ``` /// @notice address ydai = 0xdA816459F1AB5631232FE5e97a05BBBb94970c95; /// @notice uint256 pps = AlchemistV2(alchemistAddress).getUnderlyingTokensPerShare(ydai); /// @notice uint256 amtUnderlyingTokens = 5000 * 10**ydai.decimals(); /// @notice AlchemistV2(alchemistAddress).withdrawUnderlying(msg.sender, ydai, amtUnderlyingTokens / pps, msg.sender, 1); /// @notice ``` /// /// @param owner The address of the account owner to withdraw from. /// @param yieldToken The address of the yield token to withdraw. /// @param shares The number of shares to burn. /// @param recipient The address of the recipient. /// @param minimumAmountOut The minimum amount of underlying tokens that are expected to be withdrawn to `recipient`. /// /// @return amountWithdrawn The number of underlying tokens that were withdrawn to `recipient`. function withdrawUnderlyingFrom( address owner, address yieldToken, uint256 shares, address recipient, uint256 minimumAmountOut ) external returns (uint256 amountWithdrawn); /// @notice Mint `amount` debt tokens. /// /// @notice `recipient` must be non-zero or this call will revert with an {IllegalArgument} error. /// @notice `amount` must be greater than zero or this call will revert with a {IllegalArgument} error. /// /// @notice Emits a {Mint} event. /// /// @notice **_NOTE:_** This function is WHITELISTED. /// /// @notice **Example:** /// @notice ``` /// @notice uint256 amtDebt = 5000; /// @notice AlchemistV2(alchemistAddress).mint(amtDebt, msg.sender); /// @notice ``` /// /// @param amount The amount of tokens to mint. /// @param recipient The address of the recipient. function mint(uint256 amount, address recipient) external; /// @notice Mint `amount` debt tokens from the account owned by `owner` to `recipient`. /// /// @notice `recipient` must be non-zero or this call will revert with an {IllegalArgument} error. /// @notice `amount` must be greater than zero or this call will revert with a {IllegalArgument} error. /// /// @notice Emits a {Mint} event. /// /// @notice **_NOTE:_** This function is WHITELISTED. /// @notice **_NOTE:_** The caller of `mintFrom()` must have **mintAllowance()** to mint debt from the `Account` controlled by **owner** for at least the amount of **yieldTokens** that **shares** will be converted to. This can be done via the `approveMint()` or `permitMint()` methods. /// /// @notice **Example:** /// @notice ``` /// @notice uint256 amtDebt = 5000; /// @notice AlchemistV2(alchemistAddress).mintFrom(msg.sender, amtDebt, msg.sender); /// @notice ``` /// /// @param owner The address of the owner of the account to mint from. /// @param amount The amount of tokens to mint. /// @param recipient The address of the recipient. function mintFrom( address owner, uint256 amount, address recipient ) external; /// @notice Burn `amount` debt tokens to credit the account owned by `recipient`. /// /// @notice `amount` will be limited up to the amount of debt that `recipient` currently holds. /// /// @notice `recipient` must be non-zero or this call will revert with an {IllegalArgument} error. /// @notice `amount` must be greater than zero or this call will revert with a {IllegalArgument} error. /// @notice `recipient` must have non-zero debt or this call will revert with an {IllegalState} error. /// /// @notice Emits a {Burn} event. /// /// @notice **_NOTE:_** This function is WHITELISTED. /// /// @notice **Example:** /// @notice ``` /// @notice uint256 amtBurn = 5000; /// @notice AlchemistV2(alchemistAddress).burn(amtBurn, msg.sender); /// @notice ``` /// /// @param amount The amount of tokens to burn. /// @param recipient The address of the recipient. /// /// @return amountBurned The amount of tokens that were burned. function burn(uint256 amount, address recipient) external returns (uint256 amountBurned); /// @notice Repay `amount` debt using `underlyingToken` to credit the account owned by `recipient`. /// /// @notice `amount` will be limited up to the amount of debt that `recipient` currently holds. /// /// @notice `amount` must be greater than zero or this call will revert with a {IllegalArgument} error. /// @notice `recipient` must be non-zero or this call will revert with an {IllegalArgument} error. /// @notice `underlyingToken` must be enabled or this call will revert with a {TokenDisabled} error. /// @notice `amount` must be less than or equal to the current available repay limit or this call will revert with a {ReplayLimitExceeded} error. /// /// @notice Emits a {Repay} event. /// @notice **_NOTE:_** This function is WHITELISTED. /// /// @notice **Example:** /// @notice ``` /// @notice address dai = 0x6b175474e89094c44da98b954eedeac495271d0f; /// @notice uint256 amtRepay = 5000; /// @notice AlchemistV2(alchemistAddress).repay(dai, amtRepay, msg.sender); /// @notice ``` /// /// @param underlyingToken The address of the underlying token to repay. /// @param amount The amount of the underlying token to repay. /// @param recipient The address of the recipient which will receive credit. /// /// @return amountRepaid The amount of tokens that were repaid. function repay( address underlyingToken, uint256 amount, address recipient ) external returns (uint256 amountRepaid); /// @notice /// /// @notice `shares` will be limited up to an equal amount of debt that `recipient` currently holds. /// /// @notice `shares` must be greater than zero or this call will revert with a {IllegalArgument} error. /// @notice `yieldToken` must be registered or this call will revert with a {UnsupportedToken} error. /// @notice `yieldToken` must be enabled or this call will revert with a {TokenDisabled} error. /// @notice `yieldToken` underlying token must be enabled or this call will revert with a {TokenDisabled} error. /// @notice The loss in expected value of `yieldToken` must be less than the maximum permitted by the system or this call will revert with a {LossExceeded} error. /// @notice `amount` must be less than or equal to the current available liquidation limit or this call will revert with a {LiquidationLimitExceeded} error. /// /// @notice Emits a {Liquidate} event. /// /// @notice **_NOTE:_** This function is WHITELISTED. /// /// @notice **Example:** /// @notice ``` /// @notice address ydai = 0xdA816459F1AB5631232FE5e97a05BBBb94970c95; /// @notice uint256 amtSharesLiquidate = 5000 * 10**ydai.decimals(); /// @notice AlchemistV2(alchemistAddress).liquidate(ydai, amtSharesLiquidate, 1); /// @notice ``` /// /// @param yieldToken The address of the yield token to liquidate. /// @param shares The number of shares to burn for credit. /// @param minimumAmountOut The minimum amount of underlying tokens that are expected to be liquidated. /// /// @return sharesLiquidated The amount of shares that were liquidated. function liquidate( address yieldToken, uint256 shares, uint256 minimumAmountOut ) external returns (uint256 sharesLiquidated); /// @notice Burns `amount` debt tokens to credit accounts which have deposited `yieldToken`. /// /// @notice `amount` must be greater than zero or this call will revert with a {IllegalArgument} error. /// @notice `yieldToken` must be registered or this call will revert with a {UnsupportedToken} error. /// /// @notice Emits a {Donate} event. /// /// @notice **_NOTE:_** This function is WHITELISTED. /// /// @notice **Example:** /// @notice ``` /// @notice address ydai = 0xdA816459F1AB5631232FE5e97a05BBBb94970c95; /// @notice uint256 amtSharesLiquidate = 5000; /// @notice AlchemistV2(alchemistAddress).liquidate(dai, amtSharesLiquidate, 1); /// @notice ``` /// /// @param yieldToken The address of the yield token to credit accounts for. /// @param amount The amount of debt tokens to burn. function donate(address yieldToken, uint256 amount) external; /// @notice Harvests outstanding yield that a yield token has accumulated and distributes it as credit to holders. /// /// @notice `msg.sender` must be a keeper or this call will revert with an {Unauthorized} error. /// @notice `yieldToken` must be registered or this call will revert with a {UnsupportedToken} error. /// @notice The amount being harvested must be greater than zero or else this call will revert with an {IllegalState} error. /// /// @notice Emits a {Harvest} event. /// /// @param yieldToken The address of the yield token to harvest. /// @param minimumAmountOut The minimum amount of underlying tokens that are expected to be withdrawn to `recipient`. function harvest(address yieldToken, uint256 minimumAmountOut) external; }
// SPDX-License-Identifier: UNLICENSED pragma solidity >=0.5.0; /// @title IAlchemistV2AdminActions /// @author Alchemix Finance /// /// @notice Specifies admin and or sentinel actions. interface IAlchemistV2AdminActions { /// @notice Contract initialization parameters. struct InitializationParams { // The initial admin account. address admin; // The ERC20 token used to represent debt. address debtToken; // The initial transmuter or transmuter buffer. address transmuter; // The minimum collateralization ratio that an account must maintain. uint256 minimumCollateralization; // The percentage fee taken from each harvest measured in units of basis points. uint256 protocolFee; // The address that receives protocol fees. address protocolFeeReceiver; // A limit used to prevent administrators from making minting functionality inoperable. uint256 mintingLimitMinimum; // The maximum number of tokens that can be minted per period of time. uint256 mintingLimitMaximum; // The number of blocks that it takes for the minting limit to be refreshed. uint256 mintingLimitBlocks; // The address of the whitelist. address whitelist; } /// @notice Configuration parameters for an underlying token. struct UnderlyingTokenConfig { // A limit used to prevent administrators from making repayment functionality inoperable. uint256 repayLimitMinimum; // The maximum number of underlying tokens that can be repaid per period of time. uint256 repayLimitMaximum; // The number of blocks that it takes for the repayment limit to be refreshed. uint256 repayLimitBlocks; // A limit used to prevent administrators from making liquidation functionality inoperable. uint256 liquidationLimitMinimum; // The maximum number of underlying tokens that can be liquidated per period of time. uint256 liquidationLimitMaximum; // The number of blocks that it takes for the liquidation limit to be refreshed. uint256 liquidationLimitBlocks; } /// @notice Configuration parameters of a yield token. struct YieldTokenConfig { // The adapter used by the system to interop with the token. address adapter; // The maximum percent loss in expected value that can occur before certain actions are disabled measured in // units of basis points. uint256 maximumLoss; // The maximum value that can be held by the system before certain actions are disabled measured in the // underlying token. uint256 maximumExpectedValue; // The number of blocks that credit will be distributed over to depositors. uint256 creditUnlockBlocks; } /// @notice Initialize the contract. /// /// @notice `params.protocolFee` must be in range or this call will with an {IllegalArgument} error. /// @notice The minting growth limiter parameters must be valid or this will revert with an {IllegalArgument} error. For more information, see the {Limiters} library. /// /// @notice Emits an {AdminUpdated} event. /// @notice Emits a {TransmuterUpdated} event. /// @notice Emits a {MinimumCollateralizationUpdated} event. /// @notice Emits a {ProtocolFeeUpdated} event. /// @notice Emits a {ProtocolFeeReceiverUpdated} event. /// @notice Emits a {MintingLimitUpdated} event. /// /// @param params The contract initialization parameters. function initialize(InitializationParams memory params) external; /// @notice Sets the pending administrator. /// /// @notice `msg.sender` must be the admin or this call will will revert with an {Unauthorized} error. /// /// @notice Emits a {PendingAdminUpdated} event. /// /// @dev This is the first step in the two-step process of setting a new administrator. After this function is called, the pending administrator will then need to call {acceptAdmin} to complete the process. /// /// @param value the address to set the pending admin to. function setPendingAdmin(address value) external; /// @notice Allows for `msg.sender` to accepts the role of administrator. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// @notice The current pending administrator must be non-zero or this call will revert with an {IllegalState} error. /// /// @dev This is the second step in the two-step process of setting a new administrator. After this function is successfully called, this pending administrator will be reset and the new administrator will be set. /// /// @notice Emits a {AdminUpdated} event. /// @notice Emits a {PendingAdminUpdated} event. function acceptAdmin() external; /// @notice Sets an address as a sentinel. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// /// @param sentinel The address to set or unset as a sentinel. /// @param flag A flag indicating of the address should be set or unset as a sentinel. function setSentinel(address sentinel, bool flag) external; /// @notice Sets an address as a keeper. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// /// @param keeper The address to set or unset as a keeper. /// @param flag A flag indicating of the address should be set or unset as a keeper. function setKeeper(address keeper, bool flag) external; /// @notice Adds an underlying token to the system. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// /// @param underlyingToken The address of the underlying token to add. /// @param config The initial underlying token configuration. function addUnderlyingToken( address underlyingToken, UnderlyingTokenConfig calldata config ) external; /// @notice Adds a yield token to the system. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// /// @notice Emits a {AddYieldToken} event. /// @notice Emits a {TokenAdapterUpdated} event. /// @notice Emits a {MaximumLossUpdated} event. /// /// @param yieldToken The address of the yield token to add. /// @param config The initial yield token configuration. function addYieldToken(address yieldToken, YieldTokenConfig calldata config) external; /// @notice Sets an underlying token as either enabled or disabled. /// /// @notice `msg.sender` must be either the admin or a sentinel or this call will revert with an {Unauthorized} error. /// @notice `underlyingToken` must be registered or this call will revert with a {UnsupportedToken} error. /// /// @notice Emits an {UnderlyingTokenEnabled} event. /// /// @param underlyingToken The address of the underlying token to enable or disable. /// @param enabled If the underlying token should be enabled or disabled. function setUnderlyingTokenEnabled(address underlyingToken, bool enabled) external; /// @notice Sets a yield token as either enabled or disabled. /// /// @notice `msg.sender` must be either the admin or a sentinel or this call will revert with an {Unauthorized} error. /// @notice `yieldToken` must be registered or this call will revert with a {UnsupportedToken} error. /// /// @notice Emits a {YieldTokenEnabled} event. /// /// @param yieldToken The address of the yield token to enable or disable. /// @param enabled If the underlying token should be enabled or disabled. function setYieldTokenEnabled(address yieldToken, bool enabled) external; /// @notice Configures the the repay limit of `underlyingToken`. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// @notice `underlyingToken` must be registered or this call will revert with a {UnsupportedToken} error. /// /// @notice Emits a {ReplayLimitUpdated} event. /// /// @param underlyingToken The address of the underlying token to configure the repay limit of. /// @param maximum The maximum repay limit. /// @param blocks The number of blocks it will take for the maximum repayment limit to be replenished when it is completely exhausted. function configureRepayLimit( address underlyingToken, uint256 maximum, uint256 blocks ) external; /// @notice Configure the liquidation limiter of `underlyingToken`. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// @notice `underlyingToken` must be registered or this call will revert with a {UnsupportedToken} error. /// /// @notice Emits a {LiquidationLimitUpdated} event. /// /// @param underlyingToken The address of the underlying token to configure the liquidation limit of. /// @param maximum The maximum liquidation limit. /// @param blocks The number of blocks it will take for the maximum liquidation limit to be replenished when it is completely exhausted. function configureLiquidationLimit( address underlyingToken, uint256 maximum, uint256 blocks ) external; /// @notice Set the address of the transmuter. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// @notice `value` must be non-zero or this call will revert with an {IllegalArgument} error. /// /// @notice Emits a {TransmuterUpdated} event. /// /// @param value The address of the transmuter. function setTransmuter(address value) external; /// @notice Set the minimum collateralization ratio. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// /// @notice Emits a {MinimumCollateralizationUpdated} event. /// /// @param value The new minimum collateralization ratio. function setMinimumCollateralization(uint256 value) external; /// @notice Sets the fee that the protocol will take from harvests. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// @notice `value` must be in range or this call will with an {IllegalArgument} error. /// /// @notice Emits a {ProtocolFeeUpdated} event. /// /// @param value The value to set the protocol fee to measured in basis points. function setProtocolFee(uint256 value) external; /// @notice Sets the address which will receive protocol fees. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// @notice `value` must be non-zero or this call will revert with an {IllegalArgument} error. /// /// @notice Emits a {ProtocolFeeReceiverUpdated} event. /// /// @param value The address to set the protocol fee receiver to. function setProtocolFeeReceiver(address value) external; /// @notice Configures the minting limiter. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// /// @notice Emits a {MintingLimitUpdated} event. /// /// @param maximum The maximum minting limit. /// @param blocks The number of blocks it will take for the maximum minting limit to be replenished when it is completely exhausted. function configureMintingLimit(uint256 maximum, uint256 blocks) external; /// @notice Sets the rate at which credit will be completely available to depositors after it is harvested. /// /// @notice Emits a {CreditUnlockRateUpdated} event. /// /// @param yieldToken The address of the yield token to set the credit unlock rate for. /// @param blocks The number of blocks that it will take before the credit will be unlocked. function configureCreditUnlockRate(address yieldToken, uint256 blocks) external; /// @notice Sets the token adapter of a yield token. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// @notice `yieldToken` must be registered or this call will revert with a {UnsupportedToken} error. /// @notice The token that `adapter` supports must be `yieldToken` or this call will revert with a {IllegalState} error. /// /// @notice Emits a {TokenAdapterUpdated} event. /// /// @param yieldToken The address of the yield token to set the adapter for. /// @param adapter The address to set the token adapter to. function setTokenAdapter(address yieldToken, address adapter) external; /// @notice Sets the maximum expected value of a yield token that the system can hold. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// @notice `yieldToken` must be registered or this call will revert with a {UnsupportedToken} error. /// /// @param yieldToken The address of the yield token to set the maximum expected value for. /// @param value The maximum expected value of the yield token denoted measured in its underlying token. function setMaximumExpectedValue(address yieldToken, uint256 value) external; /// @notice Sets the maximum loss that a yield bearing token will permit before restricting certain actions. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// @notice `yieldToken` must be registered or this call will revert with a {UnsupportedToken} error. /// /// @dev There are two types of loss of value for yield bearing assets: temporary or permanent. The system will automatically restrict actions which are sensitive to both forms of loss when detected. For example, deposits must be restricted when an excessive loss is encountered to prevent users from having their collateral harvested from them. While the user would receive credit, which then could be exchanged for value equal to the collateral that was harvested from them, it is seen as a negative user experience because the value of their collateral should have been higher than what was originally recorded when they made their deposit. /// /// @param yieldToken The address of the yield bearing token to set the maximum loss for. /// @param value The value to set the maximum loss to. This is in units of basis points. function setMaximumLoss(address yieldToken, uint256 value) external; /// @notice Snap the expected value `yieldToken` to the current value. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// @notice `yieldToken` must be registered or this call will revert with a {UnsupportedToken} error. /// /// @dev This function should only be used in the event of a loss in the target yield-token. For example, say a third-party protocol experiences a fifty percent loss. The expected value (amount of underlying tokens) of the yield tokens being held by the system would be two times the real value that those yield tokens could be redeemed for. This function gives governance a way to realize those losses so that users can continue using the token as normal. /// /// @param yieldToken The address of the yield token to snap. function snap(address yieldToken) external; /// @notice Set the address of the V1 transfer adapter. /// /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error. /// /// @param transferAdapterAddress The address of the V1 transfer adapter to be set in the alchemist. function setTransferAdapterAddress(address transferAdapterAddress) external; /// @notice Accept debt from the V1 transfer vault adapter. /// /// @notice `msg.sender` must be a sentinal or the admin or this call will revert with an {Unauthorized} error. /// /// @param owner The owner of the account whos debt to increase. /// @param debt The amount of debt incoming from the V1 tranfer adapter. function transferDebtV1(address owner, int256 debt) external; }
// SPDX-License-Identifier: UNLICENSED pragma solidity >=0.5.0; /// @title IAlchemistV2Errors /// @author Alchemix Finance /// /// @notice Specifies errors. interface IAlchemistV2Errors { /// @notice An error which is used to indicate that an operation failed because it tried to operate on a token that the system did not recognize. /// /// @param token The address of the token. error UnsupportedToken(address token); /// @notice An error which is used to indicate that an operation failed because it tried to operate on a token that has been disabled. /// /// @param token The address of the token. error TokenDisabled(address token); /// @notice An error which is used to indicate that an operation failed because an account became undercollateralized. error Undercollateralized(); /// @notice An error which is used to indicate that an operation failed because the expected value of a yield token in the system exceeds the maximum value permitted. /// /// @param yieldToken The address of the yield token. /// @param expectedValue The expected value measured in units of the underlying token. /// @param maximumExpectedValue The maximum expected value permitted measured in units of the underlying token. error ExpectedValueExceeded(address yieldToken, uint256 expectedValue, uint256 maximumExpectedValue); /// @notice An error which is used to indicate that an operation failed because the loss that a yield token in the system exceeds the maximum value permitted. /// /// @param yieldToken The address of the yield token. /// @param loss The amount of loss measured in basis points. /// @param maximumLoss The maximum amount of loss permitted measured in basis points. error LossExceeded(address yieldToken, uint256 loss, uint256 maximumLoss); /// @notice An error which is used to indicate that a minting operation failed because the minting limit has been exceeded. /// /// @param amount The amount of debt tokens that were requested to be minted. /// @param available The amount of debt tokens which are available to mint. error MintingLimitExceeded(uint256 amount, uint256 available); /// @notice An error which is used to indicate that an repay operation failed because the repay limit for an underlying token has been exceeded. /// /// @param underlyingToken The address of the underlying token. /// @param amount The amount of underlying tokens that were requested to be repaid. /// @param available The amount of underlying tokens that are available to be repaid. error RepayLimitExceeded(address underlyingToken, uint256 amount, uint256 available); /// @notice An error which is used to indicate that an repay operation failed because the liquidation limit for an underlying token has been exceeded. /// /// @param underlyingToken The address of the underlying token. /// @param amount The amount of underlying tokens that were requested to be liquidated. /// @param available The amount of underlying tokens that are available to be liquidated. error LiquidationLimitExceeded(address underlyingToken, uint256 amount, uint256 available); /// @notice An error which is used to indicate that the slippage of a wrap or unwrap operation was exceeded. /// /// @param amount The amount of underlying or yield tokens returned by the operation. /// @param minimumAmountOut The minimum amount of the underlying or yield token that was expected when performing /// the operation. error SlippageExceeded(uint256 amount, uint256 minimumAmountOut); }
// SPDX-License-Identifier: UNLICENSED pragma solidity >=0.5.0; /// @title IAlchemistV2Immutables /// @author Alchemix Finance interface IAlchemistV2Immutables { /// @notice Returns the version of the alchemist. /// /// @return The version. function version() external view returns (string memory); /// @notice Returns the address of the debt token used by the system. /// /// @return The address of the debt token. function debtToken() external view returns (address); }
pragma solidity >=0.5.0; /// @title IAlchemistV2Events /// @author Alchemix Finance interface IAlchemistV2Events { /// @notice Emitted when the pending admin is updated. /// /// @param pendingAdmin The address of the pending admin. event PendingAdminUpdated(address pendingAdmin); /// @notice Emitted when the administrator is updated. /// /// @param admin The address of the administrator. event AdminUpdated(address admin); /// @notice Emitted when an address is set or unset as a sentinel. /// /// @param sentinel The address of the sentinel. /// @param flag A flag indicating if `sentinel` was set or unset as a sentinel. event SentinelSet(address sentinel, bool flag); /// @notice Emitted when an address is set or unset as a keeper. /// /// @param sentinel The address of the keeper. /// @param flag A flag indicating if `keeper` was set or unset as a sentinel. event KeeperSet(address sentinel, bool flag); /// @notice Emitted when an underlying token is added. /// /// @param underlyingToken The address of the underlying token that was added. event AddUnderlyingToken(address indexed underlyingToken); /// @notice Emitted when a yield token is added. /// /// @param yieldToken The address of the yield token that was added. event AddYieldToken(address indexed yieldToken); /// @notice Emitted when an underlying token is enabled or disabled. /// /// @param underlyingToken The address of the underlying token that was enabled or disabled. /// @param enabled A flag indicating if the underlying token was enabled or disabled. event UnderlyingTokenEnabled(address indexed underlyingToken, bool enabled); /// @notice Emitted when an yield token is enabled or disabled. /// /// @param yieldToken The address of the yield token that was enabled or disabled. /// @param enabled A flag indicating if the yield token was enabled or disabled. event YieldTokenEnabled(address indexed yieldToken, bool enabled); /// @notice Emitted when the repay limit of an underlying token is updated. /// /// @param underlyingToken The address of the underlying token. /// @param maximum The updated maximum repay limit. /// @param blocks The updated number of blocks it will take for the maximum repayment limit to be replenished when it is completely exhausted. event RepayLimitUpdated(address indexed underlyingToken, uint256 maximum, uint256 blocks); /// @notice Emitted when the liquidation limit of an underlying token is updated. /// /// @param underlyingToken The address of the underlying token. /// @param maximum The updated maximum liquidation limit. /// @param blocks The updated number of blocks it will take for the maximum liquidation limit to be replenished when it is completely exhausted. event LiquidationLimitUpdated(address indexed underlyingToken, uint256 maximum, uint256 blocks); /// @notice Emitted when the transmuter is updated. /// /// @param transmuter The updated address of the transmuter. event TransmuterUpdated(address transmuter); /// @notice Emitted when the minimum collateralization is updated. /// /// @param minimumCollateralization The updated minimum collateralization. event MinimumCollateralizationUpdated(uint256 minimumCollateralization); /// @notice Emitted when the protocol fee is updated. /// /// @param protocolFee The updated protocol fee. event ProtocolFeeUpdated(uint256 protocolFee); /// @notice Emitted when the protocol fee receiver is updated. /// /// @param protocolFeeReceiver The updated address of the protocol fee receiver. event ProtocolFeeReceiverUpdated(address protocolFeeReceiver); /// @notice Emitted when the minting limit is updated. /// /// @param maximum The updated maximum minting limit. /// @param blocks The updated number of blocks it will take for the maximum minting limit to be replenished when it is completely exhausted. event MintingLimitUpdated(uint256 maximum, uint256 blocks); /// @notice Emitted when the credit unlock rate is updated. /// /// @param yieldToken The address of the yield token. /// @param blocks The number of blocks that distributed credit will unlock over. event CreditUnlockRateUpdated(address yieldToken, uint256 blocks); /// @notice Emitted when the adapter of a yield token is updated. /// /// @param yieldToken The address of the yield token. /// @param tokenAdapter The updated address of the token adapter. event TokenAdapterUpdated(address yieldToken, address tokenAdapter); /// @notice Emitted when the maximum expected value of a yield token is updated. /// /// @param yieldToken The address of the yield token. /// @param maximumExpectedValue The updated maximum expected value. event MaximumExpectedValueUpdated(address indexed yieldToken, uint256 maximumExpectedValue); /// @notice Emitted when the maximum loss of a yield token is updated. /// /// @param yieldToken The address of the yield token. /// @param maximumLoss The updated maximum loss. event MaximumLossUpdated(address indexed yieldToken, uint256 maximumLoss); /// @notice Emitted when the expected value of a yield token is snapped to its current value. /// /// @param yieldToken The address of the yield token. /// @param expectedValue The updated expected value measured in the yield token's underlying token. event Snap(address indexed yieldToken, uint256 expectedValue); /// @notice Emitted when a the admin sweeps all of one reward token from the Alchemist /// /// @param rewardToken The address of the reward token. /// @param amount The amount of 'rewardToken' swept into the admin. event SweepRewardTokens(address indexed rewardToken, uint256 amount); /// @notice Emitted when a the admin sweeps all of one token from the Alchemist /// /// @param token The address of the reward token. /// @param amount The amount of 'token' swept into the admin. event SweepTokens(address indexed token, uint256 amount); /// @notice Emitted when `owner` grants `spender` the ability to mint debt tokens on its behalf. /// /// @param owner The address of the account owner. /// @param spender The address which is being permitted to mint tokens on the behalf of `owner`. /// @param amount The amount of debt tokens that `spender` is allowed to mint. event ApproveMint(address indexed owner, address indexed spender, uint256 amount); /// @notice Emitted when `owner` grants `spender` the ability to withdraw `yieldToken` from its account. /// /// @param owner The address of the account owner. /// @param spender The address which is being permitted to mint tokens on the behalf of `owner`. /// @param yieldToken The address of the yield token that `spender` is allowed to withdraw. /// @param amount The amount of shares of `yieldToken` that `spender` is allowed to withdraw. event ApproveWithdraw(address indexed owner, address indexed spender, address indexed yieldToken, uint256 amount); /// @notice Emitted when a user deposits `amount of `yieldToken` to `recipient`. /// /// @notice This event does not imply that `sender` directly deposited yield tokens. It is possible that the /// underlying tokens were wrapped. /// /// @param sender The address of the user which deposited funds. /// @param yieldToken The address of the yield token that was deposited. /// @param amount The amount of yield tokens that were deposited. /// @param recipient The address that received the deposited funds. event Deposit(address indexed sender, address indexed yieldToken, uint256 amount, address recipient); /// @notice Emitted when `shares` shares of `yieldToken` are burned to withdraw `yieldToken` from the account owned /// by `owner` to `recipient`. /// /// @notice This event does not imply that `recipient` received yield tokens. It is possible that the yield tokens /// were unwrapped. /// /// @param owner The address of the account owner. /// @param yieldToken The address of the yield token that was withdrawn. /// @param shares The amount of shares that were burned. /// @param recipient The address that received the withdrawn funds. event Withdraw(address indexed owner, address indexed yieldToken, uint256 shares, address recipient); /// @notice Emitted when `amount` debt tokens are minted to `recipient` using the account owned by `owner`. /// /// @param owner The address of the account owner. /// @param amount The amount of tokens that were minted. /// @param recipient The recipient of the minted tokens. event Mint(address indexed owner, uint256 amount, address recipient); /// @notice Emitted when `sender` burns `amount` debt tokens to grant credit to `recipient`. /// /// @param sender The address which is burning tokens. /// @param amount The amount of tokens that were burned. /// @param recipient The address that received credit for the burned tokens. event Burn(address indexed sender, uint256 amount, address recipient); /// @notice Emitted when `amount` of `underlyingToken` are repaid to grant credit to `recipient`. /// /// @param sender The address which is repaying tokens. /// @param underlyingToken The address of the underlying token that was used to repay debt. /// @param amount The amount of the underlying token that was used to repay debt. /// @param recipient The address that received credit for the repaid tokens. /// @param credit The amount of debt that was paid-off to the account owned by owner. event Repay(address indexed sender, address indexed underlyingToken, uint256 amount, address recipient, uint256 credit); /// @notice Emitted when `sender` liquidates `share` shares of `yieldToken`. /// /// @param owner The address of the account owner liquidating shares. /// @param yieldToken The address of the yield token. /// @param underlyingToken The address of the underlying token. /// @param shares The amount of the shares of `yieldToken` that were liquidated. /// @param credit The amount of debt that was paid-off to the account owned by owner. event Liquidate(address indexed owner, address indexed yieldToken, address indexed underlyingToken, uint256 shares, uint256 credit); /// @notice Emitted when `sender` burns `amount` debt tokens to grant credit to users who have deposited `yieldToken`. /// /// @param sender The address which burned debt tokens. /// @param yieldToken The address of the yield token. /// @param amount The amount of debt tokens which were burned. event Donate(address indexed sender, address indexed yieldToken, uint256 amount); /// @notice Emitted when `yieldToken` is harvested. /// /// @param yieldToken The address of the yield token that was harvested. /// @param minimumAmountOut The maximum amount of loss that is acceptable when unwrapping the underlying tokens into yield tokens, measured in basis points. /// @param totalHarvested The total amount of underlying tokens harvested. /// @param credit The total amount of debt repaid to depositors of `yieldToken`. event Harvest(address indexed yieldToken, uint256 minimumAmountOut, uint256 totalHarvested, uint256 credit); }
// SPDX-License-Identifier: UNLICENSED pragma solidity >=0.5.0; /// @title IAlchemistV2State /// @author Alchemix Finance interface IAlchemistV2State { /// @notice Defines underlying token parameters. struct UnderlyingTokenParams { // The number of decimals the token has. This value is cached once upon registering the token so it is important // that the decimals of the token are immutable or the system will begin to have computation errors. uint8 decimals; // A coefficient used to normalize the token to a value comparable to the debt token. For example, if the // underlying token is 8 decimals and the debt token is 18 decimals then the conversion factor will be // 10^10. One unit of the underlying token will be comparably equal to one unit of the debt token. uint256 conversionFactor; // A flag to indicate if the token is enabled. bool enabled; } /// @notice Defines yield token parameters. struct YieldTokenParams { // The number of decimals the token has. This value is cached once upon registering the token so it is important // that the decimals of the token are immutable or the system will begin to have computation errors. uint8 decimals; // The associated underlying token that can be redeemed for the yield-token. address underlyingToken; // The adapter used by the system to wrap, unwrap, and lookup the conversion rate of this token into its // underlying token. address adapter; // The maximum percentage loss that is acceptable before disabling certain actions. uint256 maximumLoss; // The maximum value of yield tokens that the system can hold, measured in units of the underlying token. uint256 maximumExpectedValue; // The percent of credit that will be unlocked per block. The representation of this value is a 18 decimal // fixed point integer. uint256 creditUnlockRate; // The current balance of yield tokens which are held by users. uint256 activeBalance; // The current balance of yield tokens which are earmarked to be harvested by the system at a later time. uint256 harvestableBalance; // The total number of shares that have been minted for this token. uint256 totalShares; // The expected value of the tokens measured in underlying tokens. This value controls how much of the token // can be harvested. When users deposit yield tokens, it increases the expected value by how much the tokens // are exchangeable for in the underlying token. When users withdraw yield tokens, it decreases the expected // value by how much the tokens are exchangeable for in the underlying token. uint256 expectedValue; // The current amount of credit which is will be distributed over time to depositors. uint256 pendingCredit; // The amount of the pending credit that has been distributed. uint256 distributedCredit; // The block number which the last credit distribution occurred. uint256 lastDistributionBlock; // The total accrued weight. This is used to calculate how much credit a user has been granted over time. The // representation of this value is a 18 decimal fixed point integer. uint256 accruedWeight; // A flag to indicate if the token is enabled. bool enabled; } /// @notice Gets the address of the admin. /// /// @return admin The admin address. function admin() external view returns (address admin); /// @notice Gets the address of the pending administrator. /// /// @return pendingAdmin The pending administrator address. function pendingAdmin() external view returns (address pendingAdmin); /// @notice Gets the address of the transfer adapter. /// /// @return transferAdapter The transfer adapter address. function transferAdapter() external view returns (address transferAdapter); /// @notice Gets if an address is a sentinel. /// /// @param sentinel The address to check. /// /// @return isSentinel If the address is a sentinel. function sentinels(address sentinel) external view returns (bool isSentinel); /// @notice Gets if an address is a keeper. /// /// @param keeper The address to check. /// /// @return isKeeper If the address is a keeper function keepers(address keeper) external view returns (bool isKeeper); /// @notice Gets the address of the transmuter. /// /// @return transmuter The transmuter address. function transmuter() external view returns (address transmuter); /// @notice Gets the minimum collateralization. /// /// @notice Collateralization is determined by taking the total value of collateral that a user has deposited into their account and dividing it their debt. /// /// @dev The value returned is a 18 decimal fixed point integer. /// /// @return minimumCollateralization The minimum collateralization. function minimumCollateralization() external view returns (uint256 minimumCollateralization); /// @notice Gets the protocol fee. /// /// @return protocolFee The protocol fee. function protocolFee() external view returns (uint256 protocolFee); /// @notice Gets the protocol fee receiver. /// /// @return protocolFeeReceiver The protocol fee receiver. function protocolFeeReceiver() external view returns (address protocolFeeReceiver); /// @notice Gets the address of the whitelist contract. /// /// @return whitelist The address of the whitelist contract. function whitelist() external view returns (address whitelist); /// @notice Gets the conversion rate of underlying tokens per share. /// /// @param yieldToken The address of the yield token to get the conversion rate for. /// /// @return rate The rate of underlying tokens per share. function getUnderlyingTokensPerShare(address yieldToken) external view returns (uint256 rate); /// @notice Gets the conversion rate of yield tokens per share. /// /// @param yieldToken The address of the yield token to get the conversion rate for. /// /// @return rate The rate of yield tokens per share. function getYieldTokensPerShare(address yieldToken) external view returns (uint256 rate); /// @notice Gets the supported underlying tokens. /// /// @dev The order of the entries returned by this function is not guaranteed to be consistent between calls. /// /// @return tokens The supported underlying tokens. function getSupportedUnderlyingTokens() external view returns (address[] memory tokens); /// @notice Gets the supported yield tokens. /// /// @dev The order of the entries returned by this function is not guaranteed to be consistent between calls. /// /// @return tokens The supported yield tokens. function getSupportedYieldTokens() external view returns (address[] memory tokens); /// @notice Gets if an underlying token is supported. /// /// @param underlyingToken The address of the underlying token to check. /// /// @return isSupported If the underlying token is supported. function isSupportedUnderlyingToken(address underlyingToken) external view returns (bool isSupported); /// @notice Gets if a yield token is supported. /// /// @param yieldToken The address of the yield token to check. /// /// @return isSupported If the yield token is supported. function isSupportedYieldToken(address yieldToken) external view returns (bool isSupported); /// @notice Gets information about the account owned by `owner`. /// /// @param owner The address that owns the account. /// /// @return debt The unrealized amount of debt that the account had incurred. /// @return depositedTokens The yield tokens that the owner has deposited. function accounts(address owner) external view returns (int256 debt, address[] memory depositedTokens); /// @notice Gets information about a yield token position for the account owned by `owner`. /// /// @param owner The address that owns the account. /// @param yieldToken The address of the yield token to get the position of. /// /// @return shares The amount of shares of that `owner` owns of the yield token. /// @return lastAccruedWeight The last recorded accrued weight of the yield token. function positions(address owner, address yieldToken) external view returns ( uint256 shares, uint256 lastAccruedWeight ); /// @notice Gets the amount of debt tokens `spender` is allowed to mint on behalf of `owner`. /// /// @param owner The owner of the account. /// @param spender The address which is allowed to mint on behalf of `owner`. /// /// @return allowance The amount of debt tokens that `spender` can mint on behalf of `owner`. function mintAllowance(address owner, address spender) external view returns (uint256 allowance); /// @notice Gets the amount of shares of `yieldToken` that `spender` is allowed to withdraw on behalf of `owner`. /// /// @param owner The owner of the account. /// @param spender The address which is allowed to withdraw on behalf of `owner`. /// @param yieldToken The address of the yield token. /// /// @return allowance The amount of shares that `spender` can withdraw on behalf of `owner`. function withdrawAllowance(address owner, address spender, address yieldToken) external view returns (uint256 allowance); /// @notice Gets the parameters of an underlying token. /// /// @param underlyingToken The address of the underlying token. /// /// @return params The underlying token parameters. function getUnderlyingTokenParameters(address underlyingToken) external view returns (UnderlyingTokenParams memory params); /// @notice Get the parameters and state of a yield-token. /// /// @param yieldToken The address of the yield token. /// /// @return params The yield token parameters. function getYieldTokenParameters(address yieldToken) external view returns (YieldTokenParams memory params); /// @notice Gets current limit, maximum, and rate of the minting limiter. /// /// @return currentLimit The current amount of debt tokens that can be minted. /// @return rate The maximum possible amount of tokens that can be liquidated at a time. /// @return maximum The highest possible maximum amount of debt tokens that can be minted at a time. function getMintLimitInfo() external view returns ( uint256 currentLimit, uint256 rate, uint256 maximum ); /// @notice Gets current limit, maximum, and rate of a repay limiter for `underlyingToken`. /// /// @param underlyingToken The address of the underlying token. /// /// @return currentLimit The current amount of underlying tokens that can be repaid. /// @return rate The rate at which the the current limit increases back to its maximum in tokens per block. /// @return maximum The maximum possible amount of tokens that can be repaid at a time. function getRepayLimitInfo(address underlyingToken) external view returns ( uint256 currentLimit, uint256 rate, uint256 maximum ); /// @notice Gets current limit, maximum, and rate of the liquidation limiter for `underlyingToken`. /// /// @param underlyingToken The address of the underlying token. /// /// @return currentLimit The current amount of underlying tokens that can be liquidated. /// @return rate The rate at which the function increases back to its maximum limit (tokens / block). /// @return maximum The highest possible maximum amount of debt tokens that can be liquidated at a time. function getLiquidationLimitInfo(address underlyingToken) external view returns ( uint256 currentLimit, uint256 rate, uint256 maximum ); function totalValue(address account) external view returns (uint256); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @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 amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
pragma solidity >=0.5.0; import "../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; /// @title IERC20Burnable /// @author Alchemix Finance interface IERC20Burnable is IERC20 { /// @notice Burns `amount` tokens from the balance of `msg.sender`. /// /// @param amount The amount of tokens to burn. /// /// @return If burning the tokens was successful. function burn(uint256 amount) external returns (bool); /// @notice Burns `amount` tokens from `owner`'s balance. /// /// @param owner The address to burn tokens from. /// @param amount The amount of tokens to burn. /// /// @return If burning the tokens was successful. function burnFrom(address owner, uint256 amount) external returns (bool); }
pragma solidity >=0.5.0; import "../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; /// @title IERC20Mintable /// @author Alchemix Finance interface IERC20Mintable is IERC20 { /// @notice Mints `amount` tokens to `recipient`. /// /// @param recipient The address which will receive the minted tokens. /// @param amount The amount of tokens to mint. function mint(address recipient, uint256 amount) external; }
{ "remappings": [ "\"ds-test/=\"../lib/ds-test/src/", "\"forge-std/=\"lib/forge-std/src/", "forge-std/=lib/forge-std/src/", "solmate/=lib/solmate/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "ds-test/=lib/forge-std/lib/ds-test/src/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "viaIR": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"ERC20CallFailed","type":"error"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"expectedValue","type":"uint256"},{"internalType":"uint256","name":"maximumExpectedValue","type":"uint256"}],"name":"ExpectedValueExceeded","type":"error"},{"inputs":[],"name":"IllegalArgument","type":"error"},{"inputs":[],"name":"IllegalState","type":"error"},{"inputs":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"LiquidationLimitExceeded","type":"error"},{"inputs":[],"name":"LockAlreadyClaimed","type":"error"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"loss","type":"uint256"},{"internalType":"uint256","name":"maximumLoss","type":"uint256"}],"name":"LossExceeded","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"MintingLimitExceeded","type":"error"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"result","type":"bytes"}],"name":"MulticallFailed","type":"error"},{"inputs":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"RepayLimitExceeded","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minimumAmountOut","type":"uint256"}],"name":"SlippageExceeded","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"TokenDisabled","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"Undercollateralized","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"UnsupportedToken","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"underlyingToken","type":"address"}],"name":"AddUnderlyingToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"yieldToken","type":"address"}],"name":"AddYieldToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"AdminUpdated","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":"ApproveMint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"address","name":"yieldToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ApproveWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"yieldToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"blocks","type":"uint256"}],"name":"CreditUnlockRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"yieldToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"yieldToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Donate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"yieldToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"minimumAmountOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalHarvested","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"credit","type":"uint256"}],"name":"Harvest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sentinel","type":"address"},{"indexed":false,"internalType":"bool","name":"flag","type":"bool"}],"name":"KeeperSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"yieldToken","type":"address"},{"indexed":true,"internalType":"address","name":"underlyingToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"credit","type":"uint256"}],"name":"Liquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"underlyingToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"maximum","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blocks","type":"uint256"}],"name":"LiquidationLimitUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"yieldToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"maximumExpectedValue","type":"uint256"}],"name":"MaximumExpectedValueUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"yieldToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"maximumLoss","type":"uint256"}],"name":"MaximumLossUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minimumCollateralization","type":"uint256"}],"name":"MinimumCollateralizationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maximum","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blocks","type":"uint256"}],"name":"MintingLimitUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingAdmin","type":"address"}],"name":"PendingAdminUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"protocolFeeReceiver","type":"address"}],"name":"ProtocolFeeReceiverUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"}],"name":"ProtocolFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"underlyingToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"credit","type":"uint256"}],"name":"Repay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"underlyingToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"maximum","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blocks","type":"uint256"}],"name":"RepayLimitUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sentinel","type":"address"},{"indexed":false,"internalType":"bool","name":"flag","type":"bool"}],"name":"SentinelSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"yieldToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"expectedValue","type":"uint256"}],"name":"Snap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rewardToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SweepRewardTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SweepTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"yieldToken","type":"address"},{"indexed":false,"internalType":"address","name":"tokenAdapter","type":"address"}],"name":"TokenAdapterUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"transmuter","type":"address"}],"name":"TransmuterUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"underlyingToken","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"UnderlyingTokenEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"yieldToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"yieldToken","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"YieldTokenEnabled","type":"event"},{"inputs":[],"name":"BPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FIXED_POINT_SCALAR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"accounts","outputs":[{"internalType":"int256","name":"debt","type":"int256"},{"internalType":"address[]","name":"depositedTokens","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"underlyingToken","type":"address"},{"components":[{"internalType":"uint256","name":"repayLimitMinimum","type":"uint256"},{"internalType":"uint256","name":"repayLimitMaximum","type":"uint256"},{"internalType":"uint256","name":"repayLimitBlocks","type":"uint256"},{"internalType":"uint256","name":"liquidationLimitMinimum","type":"uint256"},{"internalType":"uint256","name":"liquidationLimitMaximum","type":"uint256"},{"internalType":"uint256","name":"liquidationLimitBlocks","type":"uint256"}],"internalType":"struct IAlchemistV2AdminActions.UnderlyingTokenConfig","name":"config","type":"tuple"}],"name":"addUnderlyingToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"components":[{"internalType":"address","name":"adapter","type":"address"},{"internalType":"uint256","name":"maximumLoss","type":"uint256"},{"internalType":"uint256","name":"maximumExpectedValue","type":"uint256"},{"internalType":"uint256","name":"creditUnlockBlocks","type":"uint256"}],"internalType":"struct IAlchemistV2AdminActions.YieldTokenConfig","name":"config","type":"tuple"}],"name":"addYieldToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approveMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"approveWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"burn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"blocks","type":"uint256"}],"name":"configureCreditUnlockRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint256","name":"maximum","type":"uint256"},{"internalType":"uint256","name":"blocks","type":"uint256"}],"name":"configureLiquidationLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maximum","type":"uint256"},{"internalType":"uint256","name":"rate","type":"uint256"}],"name":"configureMintingLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint256","name":"maximum","type":"uint256"},{"internalType":"uint256","name":"blocks","type":"uint256"}],"name":"configureRepayLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertSharesToUnderlyingTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertSharesToYieldTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"convertUnderlyingTokensToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"convertUnderlyingTokensToYield","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"convertYieldTokensToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"convertYieldTokensToUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"debtToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"minimumAmountOut","type":"uint256"}],"name":"depositUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"donate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"underlyingToken","type":"address"}],"name":"getLiquidationLimitInfo","outputs":[{"internalType":"uint256","name":"currentLimit","type":"uint256"},{"internalType":"uint256","name":"rate","type":"uint256"},{"internalType":"uint256","name":"maximum","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMintLimitInfo","outputs":[{"internalType":"uint256","name":"currentLimit","type":"uint256"},{"internalType":"uint256","name":"rate","type":"uint256"},{"internalType":"uint256","name":"maximum","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"underlyingToken","type":"address"}],"name":"getRepayLimitInfo","outputs":[{"internalType":"uint256","name":"currentLimit","type":"uint256"},{"internalType":"uint256","name":"rate","type":"uint256"},{"internalType":"uint256","name":"maximum","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSupportedUnderlyingTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSupportedYieldTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"underlyingToken","type":"address"}],"name":"getUnderlyingTokenParameters","outputs":[{"components":[{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"uint256","name":"conversionFactor","type":"uint256"},{"internalType":"bool","name":"enabled","type":"bool"}],"internalType":"struct IAlchemistV2State.UnderlyingTokenParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"}],"name":"getUnderlyingTokensPerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"}],"name":"getYieldTokenParameters","outputs":[{"components":[{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"address","name":"adapter","type":"address"},{"internalType":"uint256","name":"maximumLoss","type":"uint256"},{"internalType":"uint256","name":"maximumExpectedValue","type":"uint256"},{"internalType":"uint256","name":"creditUnlockRate","type":"uint256"},{"internalType":"uint256","name":"activeBalance","type":"uint256"},{"internalType":"uint256","name":"harvestableBalance","type":"uint256"},{"internalType":"uint256","name":"totalShares","type":"uint256"},{"internalType":"uint256","name":"expectedValue","type":"uint256"},{"internalType":"uint256","name":"pendingCredit","type":"uint256"},{"internalType":"uint256","name":"distributedCredit","type":"uint256"},{"internalType":"uint256","name":"lastDistributionBlock","type":"uint256"},{"internalType":"uint256","name":"accruedWeight","type":"uint256"},{"internalType":"bool","name":"enabled","type":"bool"}],"internalType":"struct IAlchemistV2State.YieldTokenParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"}],"name":"getYieldTokensPerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"minimumAmountOut","type":"uint256"}],"name":"harvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"debtToken","type":"address"},{"internalType":"address","name":"transmuter","type":"address"},{"internalType":"uint256","name":"minimumCollateralization","type":"uint256"},{"internalType":"uint256","name":"protocolFee","type":"uint256"},{"internalType":"address","name":"protocolFeeReceiver","type":"address"},{"internalType":"uint256","name":"mintingLimitMinimum","type":"uint256"},{"internalType":"uint256","name":"mintingLimitMaximum","type":"uint256"},{"internalType":"uint256","name":"mintingLimitBlocks","type":"uint256"},{"internalType":"address","name":"whitelist","type":"address"}],"internalType":"struct IAlchemistV2AdminActions.InitializationParams","name":"params","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"underlyingToken","type":"address"}],"name":"isSupportedUnderlyingToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"}],"name":"isSupportedYieldToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"keepers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"minimumAmountOut","type":"uint256"}],"name":"liquidate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minimumCollateralization","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"mintAllowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"mintFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"normalizeDebtTokensToUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"normalizeUnderlyingTokensToDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"poke","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"yieldToken","type":"address"}],"name":"positions","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"lastAccruedWeight","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFeeReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"repay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"sentinels","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"keeper","type":"address"},{"internalType":"bool","name":"flag","type":"bool"}],"name":"setKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setMaximumExpectedValue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setMaximumLoss","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setMinimumCollateralization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"value","type":"address"}],"name":"setPendingAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"value","type":"address"}],"name":"setProtocolFeeReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sentinel","type":"address"},{"internalType":"bool","name":"flag","type":"bool"}],"name":"setSentinel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"address","name":"adapter","type":"address"}],"name":"setTokenAdapter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"transferAdapterAddress","type":"address"}],"name":"setTransferAdapterAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"value","type":"address"}],"name":"setTransmuter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setUnderlyingTokenEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setYieldTokenEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"}],"name":"snap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"totalValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"transferAdapter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"int256","name":"debt","type":"int256"}],"name":"transferDebtV1","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"transmuter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelist","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"address","name":"yieldToken","type":"address"}],"name":"withdrawAllowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"withdrawFrom","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"minimumAmountOut","type":"uint256"}],"name":"withdrawUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"yieldToken","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"minimumAmountOut","type":"uint256"}],"name":"withdrawUnderlyingFrom","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60806040523480156200001157600080fd5b50600054610100900460ff1615808015620000335750600054600160ff909116105b8062000063575062000050306200013d60201b620036f31760201c565b15801562000063575060005460ff166001145b620000cb5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840160405180910390fd5b6000805460ff191660011790558015620000ef576000805461ff0019166101001790555b801562000136576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b506200014c565b6001600160a01b03163b151590565b615dc5806200015c6000396000f3fe60806040526004361061045c5760003560e01c806365aeec141161023f578063ac9650d811610139578063e69d849d116100b6578063f45346dc1161007a578063f45346dc14610eba578063f65816b014610eda578063f851a44014610efa578063f8d8989814610f1a578063fcd3533c14610f3a57600080fd5b8063e69d849d14610e24578063e8b6571b14610e44578063edca201b14610e5a578063eeb15a9c14610e7a578063f36eebe314610e9a57600080fd5b8063b968ee3c116100fd578063b968ee3c14610d84578063bdfa9bae14610da4578063ca9e8ad614610dc4578063d1b9e85314610de4578063d6737ccf14610e0457600080fd5b8063ac9650d814610cee578063adb4a9ad14610d0e578063b0e21e8a14610d2e578063b1a997ac14610d44578063b30ac7f614610d6457600080fd5b80638127b8c2116101c75780639b4542e61161018b5780639b4542e614610c4e578063a4a5da4314610c6e578063a6459a3214610c8e578063a96cb06914610cae578063a9aa522814610cce57600080fd5b80638127b8c214610bae57806388e6f15a14610bce57806393e59dc114610bee57806394bf804d14610c0e578063991c0fea14610c2e57600080fd5b80637239d2b91161020e5780637239d2b914610ae55780637592055814610b05578063787dce3d14610b255780637c60e0cf14610b455780637c8c18ee14610b5a57600080fd5b806365aeec1414610a6557806369328dec14610a855780636987227a14610aa55780636bf9b3e714610ac557600080fd5b8063306f6e671161035b5780634dd18bf5116102d85780635ceae9c41161029c5780635ceae9c4146109b75780635e5c06e2146109d75780635ee5530214610a055780635f9efc5414610a2557806363c9249014610a4557600080fd5b80634dd18bf5146108ec57806351044b8e1461090c57806354fd4d501461092c5780635a5efc8b1461096a5780635c34586c1461099757600080fd5b8063412209381161031f578063412209381461084257806346877b1a1461086257806346bb87c2146108825780634a222635146108a25780634bd21445146108b757600080fd5b8063306f6e671461079257806330b0680b146107b257806339a51be5146107d25780633bbd64bc146107f25780633f6384fe1461082257600080fd5b806315f8b3ec116103e95780631b962c78116103ad5780631b962c78146106e25780631f3a57a014610704578063249d39e914610724578063267822471461073a57806326b00c001461077257600080fd5b806315f8b3ec146105c6578063179319011461066657806319815a321461068257806319a158d0146106a25780631a49e55e146106c257600080fd5b80630710285c116104305780630710285c1461052357806307e3a3af146105515780630cf10a6b146105715780630e18b6811461059157806313b39b9c146105a657600080fd5b8062796bf714610461578063018ee9b71461048357806302892d70146104a35780630416f9e6146104e8575b600080fd5b34801561046d57600080fd5b5061048161047c3660046152a5565b610f5a565b005b34801561048f57600080fd5b5061048161049e3660046152de565b610fc5565b3480156104af57600080fd5b506104d36104be36600461530a565b60056020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b3480156104f457600080fd5b5061050861050336600461530a565b611171565b604080519384526020840192909252908201526060016104df565b34801561052f57600080fd5b5061054361053e366004615327565b6111a9565b6040519081526020016104df565b34801561055d57600080fd5b5061054361056c36600461535c565b611436565b34801561057d57600080fd5b5061054361058c3660046152de565b611467565b34801561059d57600080fd5b5061048161148d565b3480156105b257600080fd5b506104816105c13660046152de565b611560565b3480156105d257600080fd5b5061063f6105e136600461530a565b6040805160608082018352600080835260208084018290529284018190526001600160a01b03949094168452601482529282902082519384018352805460ff9081168552600182015492850192909252600201541615159082015290565b60408051825160ff16815260208084015190820152918101511515908201526060016104df565b34801561067257600080fd5b50610543670de0b6b3a764000081565b34801561068e57600080fd5b5061048161069d36600461538a565b611573565b3480156106ae57600080fd5b506104816106bd366004615327565b6115cb565b3480156106ce57600080fd5b506104816106dd36600461535c565b61166a565b3480156106ee57600080fd5b506106f76117e5565b6040516104df91906153e7565b34801561071057600080fd5b5061054361071f3660046152de565b61184a565b34801561073057600080fd5b5061054361271081565b34801561074657600080fd5b5060045461075a906001600160a01b031681565b6040516001600160a01b0390911681526020016104df565b34801561077e57600080fd5b5061048161078d3660046152a5565b6118ac565b34801561079e57600080fd5b506104816107ad3660046152de565b611920565b3480156107be57600080fd5b506105436107cd36600461530a565b6119a5565b3480156107de57600080fd5b50600a5461075a906001600160a01b031681565b3480156107fe57600080fd5b506104d361080d36600461530a565b60066020526000908152604090205460ff1681565b34801561082e57600080fd5b5061048161083d3660046153fa565b611a6d565b34801561084e57600080fd5b5061048161085d3660046152a5565b611f1c565b34801561086e57600080fd5b5061048161087d36600461530a565b611f88565b34801561088e57600080fd5b5061054361089d3660046152de565b611ff2565b3480156108ae57600080fd5b506106f76120a6565b3480156108c357600080fd5b506108d76108d236600461535c565b612109565b604080519283526020830191909152016104df565b3480156108f857600080fd5b5061048161090736600461530a565b612147565b34801561091857600080fd5b5061048161092736600461543b565b61219d565b34801561093857600080fd5b5061095d604051806040016040528060058152602001640645c645c760db1b81525081565b6040516104df91906154d5565b34801561097657600080fd5b5061098a61098536600461530a565b6121f2565b6040516104df91906154e8565b3480156109a357600080fd5b506104816109b23660046152de565b61235a565b3480156109c357600080fd5b506105436109d236600461543b565b6123c9565b3480156109e357600080fd5b506109f76109f236600461530a565b6125b7565b6040516104df9291906155b2565b348015610a1157600080fd5b506104d3610a2036600461530a565b612641565b348015610a3157600080fd5b50610543610a403660046152de565b61264e565b348015610a5157600080fd5b50610481610a60366004615327565b61266f565b348015610a7157600080fd5b50610508610a8036600461530a565b612705565b348015610a9157600080fd5b50610543610aa036600461543b565b61272a565b348015610ab157600080fd5b50610481610ac0366004615619565b612780565b348015610ad157600080fd5b50610481610ae03660046156bf565b612a97565b348015610af157600080fd5b5060075461075a906001600160a01b031681565b348015610b1157600080fd5b50610543610b203660046152de565b612ab4565b348015610b3157600080fd5b50610481610b4036600461538a565b612ada565b348015610b5157600080fd5b50610508612b25565b348015610b6657600080fd5b50610543610b75366004615700565b6001600160a01b039283166000908152601360209081526040808320948616835260069094018152838220929094168152925290205490565b348015610bba57600080fd5b50610543610bc93660046152de565b612b46565b348015610bda57600080fd5b50610543610be936600461530a565b612b90565b348015610bfa57600080fd5b50600b5461075a906001600160a01b031681565b348015610c1a57600080fd5b50610481610c29366004615740565b612bbd565b348015610c3a57600080fd5b50610481610c4936600461530a565b612c02565b348015610c5a57600080fd5b50610481610c693660046152de565b612c38565b348015610c7a57600080fd5b50610543610c893660046152de565b612c64565b348015610c9a57600080fd5b50610543610ca9366004615765565b612c7d565b348015610cba57600080fd5b506104d3610cc936600461530a565b612cde565b348015610cda57600080fd5b50610543610ce936600461530a565b612ceb565b610d01610cfc3660046157ad565b612d18565b6040516104df9190615822565b348015610d1a57600080fd5b50610481610d2936600461530a565b612e6d565b348015610d3a57600080fd5b5061054360095481565b348015610d5057600080fd5b50610481610d5f36600461530a565b612ed7565b348015610d7057600080fd5b50610481610d7f366004615884565b612f0c565b348015610d9057600080fd5b50610543610d9f3660046158b7565b6130eb565b348015610db057600080fd5b50610543610dbf366004615765565b613158565b348015610dd057600080fd5b50610481610ddf366004615912565b6131a1565b348015610df057600080fd5b50610481610dff3660046152a5565b6131f5565b348015610e1057600080fd5b50610481610e1f36600461530a565b613259565b348015610e3057600080fd5b50610481610e3f3660046152de565b613300565b348015610e5057600080fd5b5061054360085481565b348015610e6657600080fd5b50610543610e75366004615934565b613459565b348015610e8657600080fd5b50610481610e953660046152de565b6134bb565b348015610ea657600080fd5b50601a5461075a906001600160a01b031681565b348015610ec657600080fd5b50610543610ed536600461543b565b61351c565b348015610ee657600080fd5b50610543610ef53660046152de565b613566565b348015610f0657600080fd5b5060035461075a906001600160a01b031681565b348015610f2657600080fd5b5060025461075a906001600160a01b031681565b348015610f4657600080fd5b50610543610f55366004615740565b6135f8565b610f62613702565b6001600160a01b038216600081815260056020908152604091829020805460ff19168515159081179091558251938452908301527e7a59171ef284e295f019cc84b743f8ef2d67121ae078a0e4668a7f992f6cd891015b60405180910390a15050565b610fcd61372e565b610fd5613755565b610fde82613784565b6001600160a01b0382166000908152601560205260409020610fff836137b7565b600681018054600090915561101581151561389f565b815461010090046001600160a01b03166000611033868430886138bd565b9050600061271060095483611048919061599d565b61105291906159bc565b9050600061106082846159de565b9050600061106e8583611467565b905061107a8982613981565b600a546110929086906001600160a01b031685613a30565b6007546110aa9086906001600160a01b031684613a30565b60075460405163bc04f0af60e01b81526001600160a01b038781166004830152602482018590529091169063bc04f0af90604401600060405180830381600087803b1580156110f857600080fd5b505af115801561110c573d6000803e3d6000fd5b5050604080518b8152602081018890529081018490526001600160a01b038c1692507f4534f107610758c3931de9ad1e176476fcfb8c74adf920167e1d54ee84fcfe76915060600160405180910390a25050505050505061116d6000600155565b5050565b6001600160a01b03811660009081526011602052604081208190819061119681613b4d565b6001820154915490969195509350915050565b60006111b361372e565b6111bb613bc7565b6111c760008411613c58565b6001600160a01b03808516600090815260156020526040902080549091610100909104166111f486613784565b6111fd86613c76565b61120681613cbd565b61120f86613d04565b6000611228600061121f33613d55565b9250821361389f565b600061123888610a408585612ab4565b90506000818811611249578761124b565b815b905060006112598a83612b46565b905060006112698b83308c6138bd565b90506112776000821161389f565b6001600160a01b03861660009081526012602052604081209061129982613b4d565b9050808311156112cb5787838260405163637e01ad60e01b81526004016112c2939291906159f5565b60405180910390fd5b6112d48d6137b7565b6112dd33613ecc565b60006112e98985611467565b90506112f5338f613f34565b611300338f88614004565b61131b3361130d836140a7565b61131690615a16565b6140d1565b6113288e86614107614113565b611332838561424a565b60075461134a908a906001600160a01b031686613a30565b60075460405163bc04f0af60e01b81526001600160a01b038b81166004830152602482018790529091169063bc04f0af90604401600060405180830381600087803b15801561139857600080fd5b505af11580156113ac573d6000803e3d6000fd5b505050506113b933614271565b886001600160a01b03168e6001600160a01b0316336001600160a01b03167f8246cc71ab01533b5bebc672a636df812f10637ad720797319d5741d5ebb39628985604051611411929190918252602082015260400190565b60405180910390a450939850505050505050505061142f6000600155565b9392505050565b6001600160a01b03808316600090815260136020908152604080832093851683526005909301905220545b92915050565b6001600160a01b03821660009081526014602052604081206001015461142f908361599d565b6004546114a4906001600160a01b0316151561389f565b6004546001600160a01b031633146114ce576040516282b42960e81b815260040160405180910390fd5b60048054600380546001600160a01b0383166001600160a01b031991821681179092559091169091556040519081527f54e4612788f90384e6843298d7854436f3a585b2c3831ab66abf1de63bfa6c2d9060200160405180910390a1604051600081527fa728e84b447788a55ff664fbfb5c3983925f88b80b672a1b0b8271c94b22df359060200160405180910390a1565b611568613bc7565b61116d3383836142e3565b61157b613702565b61158f670de0b6b3a7640000821015613c58565b60088190556040518181527f6f428e543b1146a0996303f0250c6fe0682874c690989e63ae53bf75bed8ef6f906020015b60405180910390a150565b6115d3613702565b6115dc8361434a565b6001600160a01b03831660009081526012602052604090206115fd90614355565b6001600160a01b038316600090815260126020526040902061162090838361436c565b60408051838152602081018390526001600160a01b038516917f0f3828e75ac2c1d15158acbdaab952f19d3a532547df5baf02f3fbbde1336d7191015b60405180910390a2505050565b611672613702565b6116f0816001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116d79190615a32565b6001600160a01b0316836001600160a01b03161461389f565b6116f982613784565b6001600160a01b03828116600090815260156020526040902060010180546001600160a01b03191691831691909117905561173782826000196143f5565b6117a5816001600160a01b0316632495a5996040518163ffffffff1660e01b8152600401602060405180830381865afa158015611778573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061179c9190615a32565b826000196143f5565b604080516001600160a01b038085168252831660208201527f0da5485c3be09248db5d9e03e12a4d5ef2df8074685a3dbb6e4e2a6c36f3e4729101610fb9565b6060601860000180548060200260200160405190810160405280929190818152602001828054801561184057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611822575b5050505050905090565b6001600160a01b0382166000908152601560205260408120600701548103611873575080611461565b61187c83614428565b6001600160a01b0384166000908152601560205260409020600701546118a2908461599d565b61142f91906159bc565b6118b46144b2565b6118bd82613784565b6001600160a01b038216600081815260156020908152604091829020600d01805460ff191685151590811790915591519182527fb15476211644e7e7a92dd4fe21d831367f5ea58130817ad39712dff67139989691015b60405180910390a25050565b611928613702565b61193460008211613c58565b61193d82613784565b61194f81670de0b6b3a76400006159bc565b6001600160a01b0383166000818152601560209081526040918290206004019390935580519182529181018390527fd5710c9f352f135b893ded82d4669bd05e5b1a7ad8d615e2ae88bc732673ad7d9101610fb9565b6001600160a01b03811660009081526013602052604081208190600301815b8154811015611a645760008260000182815481106119e4576119e4615a4f565b60009182526020808320909101546001600160a01b03908116808452601583526040808520548b84168652601385528186208387526001019094528420549094506101009092041691611a378483612c64565b9050611a438382611467565b611a4d9088615a65565b96505050505080611a5d90615a7d565b90506119c4565b50909392505050565b611a7561372e565b611a7d613702565b611a8f61271082602001351115613c58565b611a9f6000826060013511613c58565b611ab3611aad6018846144f5565b1561389f565b6000611ac2602083018361530a565b9050611b42816001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b299190615a32565b6001600160a01b0316846001600160a01b03161461389f565b611bac816001600160a01b0316632495a5996040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba79190615a32565b61434a565b604051806101e00160405280611bc185614516565b60ff168152602001826001600160a01b0316632495a5996040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c2b9190615a32565b6001600160a01b03168152602090810190611c489085018561530a565b6001600160a01b0316815260200183602001358152602001836040013581526020018360600135670de0b6b3a7640000611c8291906159bc565b81526000602080830182905260408084018390526060808501849052608080860185905260a080870186905260c080880187905260e0808901889052610100808a01899052610120998a018990526001600160a01b038e81168a5260158952988790208b518154998d015160ff9091166001600160a81b0319909a1699909917988a168202989098178855958a01516001880180546001600160a01b0319169190991617909755928801516002860155908701516003850155860151600484015585015160058301559184015160068201559083015160078201559082015160088201556101408201516009820155610160820151600a820155610180820151600b8201556101a0820151600c8201556101c090910151600d909101805460ff1916911515919091179055611db86018846145fc565b50611dd283611dca602085018561530a565b6000196143f5565b611e44816001600160a01b0316632495a5996040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e379190615a32565b611dca602085018561530a565b6040516001600160a01b038416907f7d45e47bed6d03b67d9ccb28bed226dd01d1425049e21b289d6fd54732131c8390600090a27f0da5485c3be09248db5d9e03e12a4d5ef2df8074685a3dbb6e4e2a6c36f3e47283611ea7602085018561530a565b604080516001600160a01b0393841681529290911660208301520160405180910390a1826001600160a01b03167f75bfe099d672835dbb3c09443e8493c7f306b46d695f749eb0bcf7352bef01478360200135604051611f0991815260200190565b60405180910390a25061116d6000600155565b611f246144b2565b611f2d8261434a565b6001600160a01b038216600081815260146020908152604091829020600201805460ff191685151590811790915591519182527fac216db82ec84df77384646fb0bd5f9bfd21ac8c46cfcf3a27a0d2feede572e29101611914565b611f90613702565b611fa46001600160a01b0382161515613c58565b600a80546001600160a01b0319166001600160a01b0383169081179091556040519081527f76203fab169061f503a4139455a7dfc20cc2d5a59c97d5bb810e431f4059aad7906020016115c0565b6001600160a01b03808316600090815260156020526040812060018101548154929391929116906120279060ff16600a615b7a565b816001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612065573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120899190615b89565b612093908661599d565b61209d91906159bc565b95945050505050565b60606016600001805480602002602001604051908101604052809291908181526020018280548015611840576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311611822575050505050905090565b6001600160a01b03808316600090815260136020908152604080832093851683526001840182528083205460029094019091529020545b9250929050565b61214f613702565b600480546001600160a01b0319166001600160a01b0383169081179091556040519081527fa728e84b447788a55ff664fbfb5c3983925f88b80b672a1b0b8271c94b22df35906020016115c0565b6121a561372e565b6121ad613bc7565b6121b960008311613c58565b6121cd6001600160a01b0382161515613c58565b6121d883338461465d565b6121e38383836146a2565b6121ed6000600155565b505050565b612282604051806101e00160405280600060ff16815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000151581525090565b506001600160a01b0390811660009081526015602090815260409182902082516101e081018452815460ff8082168352610100918290048716948301949094526001830154909516938101939093526002810154606084015260038101546080840152600481015460a0840152600581015460c0840152600681015460e084015260078101549383019390935260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d9092015490911615156101c082015290565b612362613702565b612370612710821115613c58565b61237982613784565b6001600160a01b03821660008181526015602052604090819020600201839055517f75bfe099d672835dbb3c09443e8493c7f306b46d695f749eb0bcf7352bef0147906119149084815260200190565b60006123d361372e565b6123db613bc7565b6123e760008411613c58565b6123fb6001600160a01b0383161515613c58565b6124048461434a565b61240d84613cbd565b61241682613ecc565b61241f82614740565b6001600160a01b0382166000908152601360205260408120549061244490821361389f565b60006124508683612ab4565b905060008186116124615785612463565b815b6001600160a01b038816600090815260116020526040812091925061248782613b4d565b9050808311156124b0578883826040516395e6194b60e01b81526004016112c2939291906159f5565b60006124bc8a85611467565b90506124cb8861130d836140a7565b6124d5838561424a565b6007546124ef908b9033906001600160a01b0316876147a9565b60075460405163bc04f0af60e01b81526001600160a01b038c81166004830152602482018790529091169063bc04f0af90604401600060405180830381600087803b15801561253d57600080fd5b505af1158015612551573d6000803e3d6000fd5b5050604080518781526001600160a01b038c81166020830152918101859052908d1692503391507f7b56e0a28bd3c28702bc74e47867c0c083a31a2b90952ed60a91c4fbd5a0e9289060600160405180910390a3509194505050505061142f6000600155565b6001600160a01b03811660009081526013602052604081206060906125db84613d55565b6003820180546040805160208084028201810190925282815291839183018282801561263057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612612575b505050505090509250925050915091565b60006114616016836144f5565b60008061265b8484613566565b9050612667848261184a565b949350505050565b612677613702565b6126808361434a565b6001600160a01b03831660009081526011602052604090206126a190614355565b6001600160a01b03831660009081526011602052604090206126c490838361436c565b60408051838152602081018390526001600160a01b038516917f86d00f048da605f1d0b35d86d59306a26293721713d9d41eef94518316eb1834910161165d565b6001600160a01b03811660009081526012602052604081208190819061119681613b4d565b600061273461372e565b61273c613bc7565b6127506001600160a01b0383161515613c58565b61275984613784565b6000612767853386866148be565b9050612774858483613a30565b905061142f6000600155565b600054610100900460ff16158080156127a05750600054600160ff909116105b806127ba5750303b1580156127ba575060005460ff166001145b61281d5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016112c2565b6000805460ff191660011790558015612840576000805461ff0019166101001790555b61285261271083608001511115613c58565b6020820151600280546001600160a01b03199081166001600160a01b0393841617909155835160038054831691841691909117905560408401516007805483169184169190911790556060840151600855608084015160095560a0840151600a80548316918416919091179055610120840151600b8054909216921691909117905560e082015161010083015160c08401516128ef92919061495d565b8051600c55602080820151600d55604080830151600e556060830151600f5560809092015160105560035491516001600160a01b0390921682527f54e4612788f90384e6843298d7854436f3a585b2c3831ab66abf1de63bfa6c2d910160405180910390a16007546040516001600160a01b0390911681527fe42bb1780117f5788c6e0042e955291223819875891ddf465b8e4fbb85a933e99060200160405180910390a17f6f428e543b1146a0996303f0250c6fe0682874c690989e63ae53bf75bed8ef6f6008546040516129c791815260200190565b60405180910390a17fd10d75876659a287a59a6ccfa2e3fff42f84d94b542837acd30bc184d562de40600954604051612a0291815260200190565b60405180910390a17f8353d6f6057b53994c31faf6112674bd84b37caab3af81c269080ae9904ebbeb8260e00151836101000151604051612a4d929190918252602082015260400190565b60405180910390a1801561116d576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001610fb9565b612a9f613bc7565b612aa882613784565b6121ed33848484614a21565b6001600160a01b03821660009081526014602052604081206001015461142f90836159bc565b612ae2613702565b612af0612710821115613c58565b60098190556040518181527fd10d75876659a287a59a6ccfa2e3fff42f84d94b542837acd30bc184d562de40906020016115c0565b6000806000612b34600c613b4d565b600d54600c5491959094509092509050565b6001600160a01b038216600090815260156020526040812060070154808203612b725782915050611461565b80612b7c85614428565b612b86908561599d565b61266791906159bc565b6001600160a01b038116600090815260156020526040812054611461908390610c899060ff16600a615b7a565b612bc561372e565b612bcd613bc7565b612bd960008311613c58565b612bed6001600160a01b0382161515613c58565b612bf83383836146a2565b61116d6000600155565b612c0a61372e565b612c12613702565b601a80546001600160a01b0319166001600160a01b038316179055600060015550565b50565b612c4061372e565b612c48614a97565b612c5182614740565b612c5b82826140d1565b612bf882614271565b600080612c718484612b46565b90506126678482611ff2565b6000612c8761372e565b612c8f613bc7565b612ca36001600160a01b0384161515613c58565b612cac85613784565b612cb585613d04565b6000612cc3863387876148be565b9050612cd1868286866138bd565b9150506126676000600155565b60006114616018836144f5565b6001600160a01b038116600090815260156020526040812054611461908390610bc99060ff16600a615b7a565b60608167ffffffffffffffff811115612d3357612d336155cb565b604051908082528060200260200182016040528015612d6657816020015b6060815260200190600190039081612d515790505b50905060005b82811015612e665760008030868685818110612d8a57612d8a615a4f565b9050602002810190612d9c9190615ba2565b604051612daa929190615be9565b600060405180830381855af49150503d8060008114612de5576040519150601f19603f3d011682016040523d82523d6000602084013e612dea565b606091505b509150915081612e3557858584818110612e0657612e06615a4f565b9050602002810190612e189190615ba2565b8260405163070c497560e21b81526004016112c293929190615bf9565b80848481518110612e4857612e48615a4f565b6020026020010181905250505080612e5f90615a7d565b9050612d6c565b5092915050565b612e75613702565b612e896001600160a01b0382161515613c58565b600780546001600160a01b0319166001600160a01b0383169081179091556040519081527fe42bb1780117f5788c6e0042e955291223819875891ddf465b8e4fbb85a933e9906020016115c0565b612edf61372e565b612ee7613bc7565b612ef081614ac1565b612ef981613ecc565b612f0281614740565b612c356000600155565b612f1461372e565b612f1c613702565b612f2a611aad6016846144f5565b6000612f3583614516565b600254909150600090612f50906001600160a01b0316614516565b9050612f648160ff168360ff161115613c58565b60405180606001604052808360ff1681526020018383612f849190615c37565b612f8f90600a615b7a565b8152600060209182018190526001600160a01b0387168152601482526040908190208351815460ff90911660ff1991821617825584840151600183015593820151600290910180549115159190941617909255612ff5919085013590850135853561495d565b6001600160a01b038516600090815260116020908152604091829020835181559083015160018201559082015160028201556060808301516003830155608092830151600490920191909155613057918501359060a08601359086013561495d565b6001600160a01b03851660009081526012602090815260409182902083518155908301516001820155908201516002820155606082015160038201556080909101516004909101556130aa6016856145fc565b506040516001600160a01b038516907f3a0e2b79f1e0c4867b78173318e64bad2dbf7be510d51198d37900e52de2bb8c90600090a2505061116d6000600155565b60006130f561372e565b6130fd613bc7565b6131116001600160a01b0384161515613c58565b61311a85613784565b61312385613d04565b61312f86338787614b29565b600061313d868887876148be565b905061314b868286866138bd565b91505061209d6000600155565b600061316261372e565b61316a613bc7565b61317e6001600160a01b0384161515613c58565b61318785613784565b6000613194868685614b6e565b9050612cd1868286614c49565b6131a9613702565b6131b3600c614355565b6131bf600c838361436c565b60408051838152602081018390527f8353d6f6057b53994c31faf6112674bd84b37caab3af81c269080ae9904ebbeb9101610fb9565b6131fd613702565b6001600160a01b038216600081815260066020908152604091829020805460ff19168515159081179091558251938452908301527f8dd62d4e1f60b96148552898e743aa2b571686baa26f4f1b647565dc3996c1a79101610fb9565b61326161372e565b613269613702565b61327281613784565b6001600160a01b038116600090815260156020526040812060050154613299908390611ff2565b6001600160a01b0383166000818152601560205260409081902060080183905551919250907fccda4b864ea094cd2323d3a440088f1171a7c3e5c46d87f6baafabe470d614da906132ed9084815260200190565b60405180910390a250612c356000600155565b61330861372e565b613310613bc7565b61331c60008211613c58565b61332582614d50565b61332e33614740565b3360009081526013602090815260408083206001600160a01b0386168452600101825280832054601590925282206007015461336a91906159de565b90508061337f670de0b6b3a76400008461599d565b61338991906159bc565b6001600160a01b0384166000908152601560205260408120600c0180549091906133b4908490615a65565b90915550506001600160a01b038084166000818152601560209081526040808320600c015433808552601384528285209585526002958601909352922091909155905461340392169084614dd0565b61340e600c83614e03565b6040518281526001600160a01b0384169033907f88dcaca629d63d86330e97adc358b13dd0ebd703239aea96b7ea2fb331b16f4e9060200160405180910390a35061116d6000600155565b600061346361372e565b61346b613bc7565b61347f6001600160a01b0383161515613c58565b61348884613784565b61349485338686614b29565b60006134a2858786866148be565b90506134af858483613a30565b90506126676000600155565b6134c3613702565b6134cc82613784565b6001600160a01b03821660008181526015602052604090819020600301839055517f4bd29da0a205336da3aaf81eada8ef63426a1c923b4a434e54ac7f898fa53c9a906119149084815260200190565b600061352661372e565b61352e613bc7565b6135426001600160a01b0383161515613c58565b61354b84613784565b6000613558858585614c49565b9050612774853330876147a9565b6001600160a01b0380831660009081526015602090815260408083206001810154825163501ad8ff60e11b8152925194959194911692839263a035b1fe92600480830193928290030181865afa1580156135c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135e89190615b89565b82546120899060ff16600a615b7a565b600061360261372e565b61360a613bc7565b61361660008411613c58565b61362a6001600160a01b0383161515613c58565b61363382613ecc565b61363c82614740565b6001600160a01b0382166000908152601360205260408120549061366190821361389f565b60008185116136705784613672565b815b90506136818461130d836140a7565b600254613698906001600160a01b03163383614dd0565b6136a3600c82614e03565b604080518281526001600160a01b038616602082015233917fdbdf9b8e4b75e75b162d151ec8fc7f0561cabab5fcccfa2600be62223e4300c4910160405180910390a29150506114616000600155565b6001600160a01b03163b151590565b6003546001600160a01b0316331461372c576040516282b42960e81b815260040160405180910390fd5b565b6001541561374f57604051632ecfcaf960e01b815260040160405180910390fd5b60018055565b3360009081526006602052604090205460ff1661372c576040516282b42960e81b815260040160405180910390fd5b61378f6018826144f5565b612c3557604051635f8b555b60e11b81526001600160a01b03821660048201526024016112c2565b6001600160a01b038116600090815260156020526040812060050154908190036137df575050565b60006137eb8383611ff2565b6001600160a01b0384166000908152601560205260409020600801549091508082116138175750505050565b600061382785610ef584866159de565b905080600003613838575050505050565b6001600160a01b038516600090815260156020526040812060050180548392906138639084906159de565b90915550506001600160a01b03851660009081526015602052604081206006018054839290613893908490615a65565b90915550505050505050565b80612c3557604051634a613c4160e01b815260040160405180910390fd5b6001600160a01b03848116600090815260156020526040808220600101549051637647691d60e01b81526004810187905285841660248201529192169082908290637647691d906044016020604051808303816000875af1158015613926573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061394a9190615b89565b905083811015613977576040516371c4efed60e01b815260048101829052602481018590526044016112c2565b9695505050505050565b6001600160a01b03821660009081526015602052604081206009810154600a82015491929091906139b186614e1a565b905060006139bf8284615a65565b6139c990856159de565b90508115613a0b5760078501546139e8670de0b6b3a76400008461599d565b6139f291906159bc565b85600c016000828254613a059190615a65565b90915550505b613a158187615a65565b600986015550506000600a840155505043600b909101555050565b6040516001600160a01b03838116602483015260448201839052600091829186169063a9059cbb60e01b906064015b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051613a9d9190615c5a565b6000604051808303816000865af19150503d8060008114613ada576040519150601f19603f3d011682016040523d82523d6000602084013e613adf565b606091505b5091509150846001600160a01b03163b60001480613afb575081155b80613b225750805115801590613b22575080806020019051810190613b209190615c76565b155b15613b465784828260405163e7e40b5b60e01b81526004016112c293929190615c93565b5050505050565b600080826003015443613b6091906159de565b905080600003613b735750506002015490565b6000670de0b6b3a7640000846001015483613b8e919061599d565b613b9891906159bc565b90506000818560020154613bac9190615a65565b85549091508111613bbd578061209d565b5050915492915050565b333203613bd057565b600b54604051633af32abf60e01b81523360048201526001600160a01b0390911690633af32abf90602401602060405180830381865afa158015613c18573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c3c9190615c76565b61372c576040516282b42960e81b815260040160405180910390fd5b80612c3557604051630134249960e71b815260040160405180910390fd5b6001600160a01b0381166000908152601560205260409020600d015460ff16612c35576040516303f585af60e31b81526001600160a01b03821660048201526024016112c2565b6001600160a01b03811660009081526014602052604090206002015460ff16612c35576040516303f585af60e31b81526001600160a01b03821660048201526024016112c2565b6000613d0f82614ec2565b6001600160a01b038316600090815260156020526040902060020154909150808211156121ed57828282604051635c1bc28160e01b81526004016112c2939291906159f5565b6001600160a01b0381166000908152601360205260408120805490600301825b8154811015611a64576000826000018281548110613d9557613d95615a4f565b60009182526020808320909101546001600160a01b03908116808452601583526040808520600c0154928b168552601384528085208286526002019093529183205491935091613de484614e1a565b905060008111613df5576000613e2d565b6001600160a01b038416600090815260156020526040902060070154613e23670de0b6b3a76400008361599d565b613e2d91906159bc565b613e379084615a65565b9250818303613e495750505050613ebc565b6001600160a01b03808a1660009081526013602090815260408083209388168352600190930190529081205490670de0b6b3a764000082613e8a86886159de565b613e94919061599d565b613e9e91906159bc565b9050613ea9816140a7565b613eb3908a615cbf565b98505050505050505b613ec581615a7d565b9050613d75565b6001600160a01b0381166000908152601360205260408120600301905b81548110156121ed57613f24826000018281548110613f0a57613f0a615a4f565b6000918252602090912001546001600160a01b0316614d50565b613f2d81615a7d565b9050613ee9565b6001600160a01b038083166000908152601360209081526040808320938516835260158252808320600c01546002850190925290912054808203613f79575050505050565b6001600160a01b038416600090815260018401602052604081205490670de0b6b3a764000082613fa985876159de565b613fb3919061599d565b613fbd91906159bc565b9050613fc8816140a7565b856000016000828254613fdb9190615cbf565b9091555050506001600160a01b0390941660009081526002909301602052506040909120555050565b6001600160a01b0380841660009081526013602090815260408083209386168352600184019091528120805484929061403e9084906159de565b90915550506001600160a01b0383166000908152601560205260408120600701805484929061406e9084906159de565b90915550506001600160a01b038316600090815260018201602052604081205490036140a157613b466003820184614fd3565b50505050565b6000600160ff1b82106140cd57604051630134249960e71b815260040160405180910390fd5b5090565b6001600160a01b03821660009081526013602052604081208054909183918391906140fd908490615cfe565b9091555050505050565b600061142f82846159de565b6001600160a01b03808416600090815260156020908152604080832081516101e081018352815460ff8082168352610100918290048816958301959095526001830154909616928101929092526002810154606083015260038101546080830152600481015460a0830152600581015460c0830152600681015460e083015260078101549482019490945260088401546101208201526009840154610140820152600a840154610160820152600b840154610180820152600c8401546101a0820152600d909301541615156101c08301526141ee8585611ff2565b905060006142048360c00151868663ffffffff16565b9050600061421b846101200151848763ffffffff16565b6001600160a01b0390971660009081526015602052604090206005810192909255506008019490945550505050565b600061425583613b4d565b905061426182826159de565b6002840155505043600390910155565b6001600160a01b038116600090815260136020526040812054908113614295575050565b600081670de0b6b3a76400006142aa856119a5565b6142b4919061599d565b6142be91906159bc565b90506008548110156121ed5760405163fddafdf560e01b815260040160405180910390fd5b6001600160a01b03838116600081815260136020908152604080832094871680845260058601835292819020869055518581529192917f09cd9a2cf8d0dd3936aef63dae78b3085d8abaaec9311145115b15e5e3eaea41910160405180910390a350505050565b61378f6016826144f5565b61435e81613b4d565b600282015543600390910155565b611c2081111561438f57604051630134249960e71b815260040160405180910390fd5b82600401548210156143b457604051630134249960e71b815260040160405180910390fd5b81836002015411156143c857600283018290555b818355806143de670de0b6b3a76400008461599d565b6143e891906159bc565b8360010181905550505050565b6040516001600160a01b03838116602483015260448201839052600091829186169063095ea7b360e01b90606401613a5f565b6001600160a01b03811660009081526015602052604081206005810154808303614453579392505050565b600061445f8583611ff2565b6008840154909150808211614478575090949350505050565b600061448887610ef584866159de565b90508060000361449d57509195945050505050565b6144a781856159de565b979650505050505050565b6003546001600160a01b031633036144c657565b3360009081526005602052604090205460ff1661372c576040516282b42960e81b815260040160405180910390fd5b6001600160a01b031660009081526001919091016020526040902054151590565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b1790529051600091829182916001600160a01b0386169161455c9190615c5a565b600060405180830381855afa9150503d8060008114614597576040519150601f19603f3d011682016040523d82523d6000602084013e61459c565b606091505b5091509150836001600160a01b03163b600014806145b8575081155b806145c4575060208151105b156145e85783828260405163e7e40b5b60e01b81526004016112c293929190615c93565b808060200190518101906126679190615d3f565b600061460883836144f5565b1561461557506000611461565b508154600180820184556000848152602080822090930180546001600160a01b0319166001600160a01b03959095169485179055845493815293810190915260409092205590565b6001600160a01b038084166000908152601360209081526040808320938616835260058401909152812080548492906146979084906159de565b909155505050505050565b6146ab82615119565b6146b483614ac1565b6146bd83613ecc565b6146c683614740565b6146d383611316846140a7565b6146dc83614271565b6146e7600c8361424a565b6002546146fe906001600160a01b03168284615152565b604080518381526001600160a01b0383811660208301528516917fbcad3d7d3dfccb90d49c6063bf70f828901fefc88937d90af74e58e6e55bc39d910161165d565b6001600160a01b0381166000908152601360205260408120600301905b81548110156121ed576147998383600001838154811061477f5761477f615a4f565b6000918252602090912001546001600160a01b0316613f34565b6147a281615a7d565b905061475d565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b179052915160009283929088169161480d9190615c5a565b6000604051808303816000865af19150503d806000811461484a576040519150601f19603f3d011682016040523d82523d6000602084013e61484f565b606091505b5091509150856001600160a01b03163b6000148061486b575081155b8061489257508051158015906148925750808060200190518101906148909190615c76565b155b156148b65785828260405163e7e40b5b60e01b81526004016112c293929190615c93565b505050505050565b60006148c984614ac1565b6148d284613ecc565b60006148de8685612b46565b90506148e985614740565b6148f4858786614004565b6149018682614107614113565b61490a85614271565b604080518581526001600160a01b03858116602083015280891692908816917f3ed4ee04a905a278b050a856bbe7ddaaf327a30514373e65aa6103beeae488c3910160405180910390a395945050505050565b61498f6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b611c208311156149b257604051630134249960e71b815260040160405180910390fd5b818410156149d357604051630134249960e71b815260040160405180910390fd5b6040518060a0016040528085815260200184670de0b6b3a7640000876149f9919061599d565b614a0391906159bc565b81526020018581526020014381526020018381525090509392505050565b6001600160a01b038481166000818152601360209081526040808320888616808552600682018452828520968916808652968452938290208790559051868152909493917f14d43ec44802545adffd2e9b5d923acf7ae9106d7774f3fd01a3af424e0f262e910160405180910390a45050505050565b601a546001600160a01b0316331461372c576040516282b42960e81b815260040160405180910390fd5b6001600160a01b0381166000908152601360205260408120600301905b81548110156121ed57614b19826000018281548110614aff57614aff615a4f565b6000918252602090912001546001600160a01b03166137b7565b614b2281615a7d565b9050614ade565b6001600160a01b0380851660009081526013602090815260408083208785168452600681018352818420948716845293909152812080548492906138939084906159de565b6001600160a01b03808416600090815260156020526040812060018101548154929391929082169161010090910416614ba9813330896147a9565b604051629dd64160e51b8152600481018790523060248201526000906001600160a01b038416906313bac820906044016020604051808303816000875af1158015614bf8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c1c9190615b89565b9050858110156144a7576040516371c4efed60e01b815260048101829052602481018790526044016112c2565b6000614c5760008411613c58565b6001600160a01b0380851660009081526015602052604090208054909161010090910416614c8486613c76565b614c8d81613cbd565b614c9686613d04565b614c9f866137b7565b614ca884613ecc565b614cb28487613f34565b6000614cbf858888615185565b9050614cce8787615266614113565b60038301546008840154811015614cfe5787878260405163a3528cf560e01b81526004016112c2939291906159f5565b604080518881526001600160a01b0388811660208301528a169133917f364bb76a44233df8584c690de6da7810626a5e77192f3ebc942c35bcb1add24f910160405180910390a3509695505050505050565b6001600160a01b038116600090815260156020526040812090614d7283614e1a565b905080600003614d8157505050565b6007820154614d98670de0b6b3a76400008361599d565b614da291906159bc565b82600c016000828254614db59190615a65565b925050819055508082600a0160008282546140fd9190615a65565b6040516001600160a01b03838116602483015260448201839052600091829186169063079cc67960e41b90606401613a5f565b6000614e0e83613b4d565b90506142618282615a65565b6001600160a01b03811660009081526015602052604081206009810154808303614e48575060009392505050565b6004820154600a830154600b840154600083614e6483436159de565b614e6e919061599d565b9050670de0b6b3a76400008110614e8e57614e8983866159de565b614eb6565b82670de0b6b3a7640000614ea2838861599d565b614eac91906159bc565b614eb691906159de565b98975050505050505050565b6001600160a01b03808216600090815260156020908152604080832081516101e081018352815460ff8082168352610100918290048816958301959095526001830154909616928101929092526002810154606083015260038101546080830152600481015460a0830152600581015460c08301819052600682015460e084015260078201549583019590955260088101546101208301526009810154610140830152600a810154610160830152600b810154610180830152600c8101546101a0830152600d015490911615156101c082015290918290614fa4908590611ff2565b610120830151909150818111614fbb57600061209d565b80612710614fc984836159de565b612093919061599d565b6001600160a01b0381166000908152600183016020526040812054808203614fff576000915050611461565b8061500981615d62565b85549092506000915061501e906001906159de565b90508082146150be57600085600001828154811061503e5761503e615a4f565b60009182526020909120015486546001600160a01b039091169150819087908590811061506d5761506d615a4f565b600091825260209091200180546001600160a01b0319166001600160a01b03929092169190911790556150a1836001615a65565b6001600160a01b0390911660009081526001870160205260409020555b84548590806150cf576150cf615d79565b60008281526020808220600019908401810180546001600160a01b03191690559092019092556001600160a01b039590951681526001958601909452505060408220919091555090565b6000615125600c613b4d565b90508082111561116d576040516313d73d6b60e01b815260048101839052602481018290526044016112c2565b6040516001600160a01b0383811660248301526044820183905260009182918616906340c10f1960e01b90606401613a5f565b600080615192848461184a565b6001600160a01b03808716600090815260136020908152604080832093891683526001909301905290812054919250036151ed576001600160a01b03851660009081526013602052604090206151eb90600301856145fc565b505b6001600160a01b0380861660009081526013602090815260408083209388168352600190930190529081208054839290615228908490615a65565b90915550506001600160a01b03841660009081526015602052604081206007018054839290615258908490615a65565b909155509095945050505050565b600061142f8284615a65565b6001600160a01b0381168114612c3557600080fd5b803561529281615272565b919050565b8015158114612c3557600080fd5b600080604083850312156152b857600080fd5b82356152c381615272565b915060208301356152d381615297565b809150509250929050565b600080604083850312156152f157600080fd5b82356152fc81615272565b946020939093013593505050565b60006020828403121561531c57600080fd5b813561142f81615272565b60008060006060848603121561533c57600080fd5b833561534781615272565b95602085013595506040909401359392505050565b6000806040838503121561536f57600080fd5b823561537a81615272565b915060208301356152d381615272565b60006020828403121561539c57600080fd5b5035919050565b600081518084526020808501945080840160005b838110156153dc5781516001600160a01b0316875295820195908201906001016153b7565b509495945050505050565b60208152600061142f60208301846153a3565b60008082840360a081121561540e57600080fd5b833561541981615272565b92506080601f198201121561542d57600080fd5b506020830190509250929050565b60008060006060848603121561545057600080fd5b833561545b81615272565b925060208401359150604084013561547281615272565b809150509250925092565b60005b83811015615498578181015183820152602001615480565b838111156140a15750506000910152565b600081518084526154c181602086016020860161547d565b601f01601f19169290920160200192915050565b60208152600061142f60208301846154a9565b815160ff1681526101e08101602083015161550e60208401826001600160a01b03169052565b50604083015161552960408401826001600160a01b03169052565b50606083810151908301526080808401519083015260a0808401519083015260c0808401519083015260e08084015190830152610100808401519083015261012080840151908301526101408084015190830152610160808401519083015261018080840151908301526101a080840151908301526101c0928301511515929091019190915290565b82815260406020820152600061266760408301846153a3565b634e487b7160e01b600052604160045260246000fd5b604051610140810167ffffffffffffffff8111828210171561561357634e487b7160e01b600052604160045260246000fd5b60405290565b6000610140828403121561562c57600080fd5b6156346155e1565b61563d83615287565b815261564b60208401615287565b602082015261565c60408401615287565b6040820152606083013560608201526080830135608082015261568160a08401615287565b60a082015260c083013560c082015260e083013560e08201526101008084013581830152506101206156b4818501615287565b908201529392505050565b6000806000606084860312156156d457600080fd5b83356156df81615272565b925060208401356156ef81615272565b929592945050506040919091013590565b60008060006060848603121561571557600080fd5b833561572081615272565b9250602084013561573081615272565b9150604084013561547281615272565b6000806040838503121561575357600080fd5b8235915060208301356152d381615272565b6000806000806080858703121561577b57600080fd5b843561578681615272565b935060208501359250604085013561579d81615272565b9396929550929360600135925050565b600080602083850312156157c057600080fd5b823567ffffffffffffffff808211156157d857600080fd5b818501915085601f8301126157ec57600080fd5b8135818111156157fb57600080fd5b8660208260051b850101111561581057600080fd5b60209290920196919550909350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561587757603f198886030184526158658583516154a9565b94509285019290850190600101615849565b5092979650505050505050565b60008082840360e081121561589857600080fd5b83356158a381615272565b925060c0601f198201121561542d57600080fd5b600080600080600060a086880312156158cf57600080fd5b85356158da81615272565b945060208601356158ea81615272565b935060408601359250606086013561590181615272565b949793965091946080013592915050565b6000806040838503121561592557600080fd5b50508035926020909101359150565b6000806000806080858703121561594a57600080fd5b843561595581615272565b9350602085013561596581615272565b925060408501359150606085013561597c81615272565b939692955090935050565b634e487b7160e01b600052601160045260246000fd5b60008160001904831182151516156159b7576159b7615987565b500290565b6000826159d957634e487b7160e01b600052601260045260246000fd5b500490565b6000828210156159f0576159f0615987565b500390565b6001600160a01b039390931683526020830191909152604082015260600190565b6000600160ff1b8201615a2b57615a2b615987565b5060000390565b600060208284031215615a4457600080fd5b815161142f81615272565b634e487b7160e01b600052603260045260246000fd5b60008219821115615a7857615a78615987565b500190565b600060018201615a8f57615a8f615987565b5060010190565b600181815b80851115615ad1578160001904821115615ab757615ab7615987565b80851615615ac457918102915b93841c9390800290615a9b565b509250929050565b600082615ae857506001611461565b81615af557506000611461565b8160018114615b0b5760028114615b1557615b31565b6001915050611461565b60ff841115615b2657615b26615987565b50506001821b611461565b5060208310610133831016604e8410600b8410161715615b54575081810a611461565b615b5e8383615a96565b8060001904821115615b7257615b72615987565b029392505050565b600061142f60ff841683615ad9565b600060208284031215615b9b57600080fd5b5051919050565b6000808335601e19843603018112615bb957600080fd5b83018035915067ffffffffffffffff821115615bd457600080fd5b60200191503681900382131561214057600080fd5b8183823760009101908152919050565b60408152826040820152828460608301376000606084830101526000601f19601f8501168201606083820301602084015261397760608201856154a9565b600060ff821660ff841680821015615c5157615c51615987565b90039392505050565b60008251615c6c81846020870161547d565b9190910192915050565b600060208284031215615c8857600080fd5b815161142f81615297565b6001600160a01b0384168152821515602082015260606040820181905260009061209d908301846154a9565b60008083128015600160ff1b850184121615615cdd57615cdd615987565b6001600160ff1b0384018313811615615cf857615cf8615987565b50500390565b600080821280156001600160ff1b0384900385131615615d2057615d20615987565b600160ff1b8390038412811615615d3957615d39615987565b50500190565b600060208284031215615d5157600080fd5b815160ff8116811461142f57600080fd5b600081615d7157615d71615987565b506000190190565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220910f37758d901447b5ac7058ca37f362fad29d46491def08d4193ef8b7679e8764736f6c634300080d0033
Deployed Bytecode
0x60806040526004361061045c5760003560e01c806365aeec141161023f578063ac9650d811610139578063e69d849d116100b6578063f45346dc1161007a578063f45346dc14610eba578063f65816b014610eda578063f851a44014610efa578063f8d8989814610f1a578063fcd3533c14610f3a57600080fd5b8063e69d849d14610e24578063e8b6571b14610e44578063edca201b14610e5a578063eeb15a9c14610e7a578063f36eebe314610e9a57600080fd5b8063b968ee3c116100fd578063b968ee3c14610d84578063bdfa9bae14610da4578063ca9e8ad614610dc4578063d1b9e85314610de4578063d6737ccf14610e0457600080fd5b8063ac9650d814610cee578063adb4a9ad14610d0e578063b0e21e8a14610d2e578063b1a997ac14610d44578063b30ac7f614610d6457600080fd5b80638127b8c2116101c75780639b4542e61161018b5780639b4542e614610c4e578063a4a5da4314610c6e578063a6459a3214610c8e578063a96cb06914610cae578063a9aa522814610cce57600080fd5b80638127b8c214610bae57806388e6f15a14610bce57806393e59dc114610bee57806394bf804d14610c0e578063991c0fea14610c2e57600080fd5b80637239d2b91161020e5780637239d2b914610ae55780637592055814610b05578063787dce3d14610b255780637c60e0cf14610b455780637c8c18ee14610b5a57600080fd5b806365aeec1414610a6557806369328dec14610a855780636987227a14610aa55780636bf9b3e714610ac557600080fd5b8063306f6e671161035b5780634dd18bf5116102d85780635ceae9c41161029c5780635ceae9c4146109b75780635e5c06e2146109d75780635ee5530214610a055780635f9efc5414610a2557806363c9249014610a4557600080fd5b80634dd18bf5146108ec57806351044b8e1461090c57806354fd4d501461092c5780635a5efc8b1461096a5780635c34586c1461099757600080fd5b8063412209381161031f578063412209381461084257806346877b1a1461086257806346bb87c2146108825780634a222635146108a25780634bd21445146108b757600080fd5b8063306f6e671461079257806330b0680b146107b257806339a51be5146107d25780633bbd64bc146107f25780633f6384fe1461082257600080fd5b806315f8b3ec116103e95780631b962c78116103ad5780631b962c78146106e25780631f3a57a014610704578063249d39e914610724578063267822471461073a57806326b00c001461077257600080fd5b806315f8b3ec146105c6578063179319011461066657806319815a321461068257806319a158d0146106a25780631a49e55e146106c257600080fd5b80630710285c116104305780630710285c1461052357806307e3a3af146105515780630cf10a6b146105715780630e18b6811461059157806313b39b9c146105a657600080fd5b8062796bf714610461578063018ee9b71461048357806302892d70146104a35780630416f9e6146104e8575b600080fd5b34801561046d57600080fd5b5061048161047c3660046152a5565b610f5a565b005b34801561048f57600080fd5b5061048161049e3660046152de565b610fc5565b3480156104af57600080fd5b506104d36104be36600461530a565b60056020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b3480156104f457600080fd5b5061050861050336600461530a565b611171565b604080519384526020840192909252908201526060016104df565b34801561052f57600080fd5b5061054361053e366004615327565b6111a9565b6040519081526020016104df565b34801561055d57600080fd5b5061054361056c36600461535c565b611436565b34801561057d57600080fd5b5061054361058c3660046152de565b611467565b34801561059d57600080fd5b5061048161148d565b3480156105b257600080fd5b506104816105c13660046152de565b611560565b3480156105d257600080fd5b5061063f6105e136600461530a565b6040805160608082018352600080835260208084018290529284018190526001600160a01b03949094168452601482529282902082519384018352805460ff9081168552600182015492850192909252600201541615159082015290565b60408051825160ff16815260208084015190820152918101511515908201526060016104df565b34801561067257600080fd5b50610543670de0b6b3a764000081565b34801561068e57600080fd5b5061048161069d36600461538a565b611573565b3480156106ae57600080fd5b506104816106bd366004615327565b6115cb565b3480156106ce57600080fd5b506104816106dd36600461535c565b61166a565b3480156106ee57600080fd5b506106f76117e5565b6040516104df91906153e7565b34801561071057600080fd5b5061054361071f3660046152de565b61184a565b34801561073057600080fd5b5061054361271081565b34801561074657600080fd5b5060045461075a906001600160a01b031681565b6040516001600160a01b0390911681526020016104df565b34801561077e57600080fd5b5061048161078d3660046152a5565b6118ac565b34801561079e57600080fd5b506104816107ad3660046152de565b611920565b3480156107be57600080fd5b506105436107cd36600461530a565b6119a5565b3480156107de57600080fd5b50600a5461075a906001600160a01b031681565b3480156107fe57600080fd5b506104d361080d36600461530a565b60066020526000908152604090205460ff1681565b34801561082e57600080fd5b5061048161083d3660046153fa565b611a6d565b34801561084e57600080fd5b5061048161085d3660046152a5565b611f1c565b34801561086e57600080fd5b5061048161087d36600461530a565b611f88565b34801561088e57600080fd5b5061054361089d3660046152de565b611ff2565b3480156108ae57600080fd5b506106f76120a6565b3480156108c357600080fd5b506108d76108d236600461535c565b612109565b604080519283526020830191909152016104df565b3480156108f857600080fd5b5061048161090736600461530a565b612147565b34801561091857600080fd5b5061048161092736600461543b565b61219d565b34801561093857600080fd5b5061095d604051806040016040528060058152602001640645c645c760db1b81525081565b6040516104df91906154d5565b34801561097657600080fd5b5061098a61098536600461530a565b6121f2565b6040516104df91906154e8565b3480156109a357600080fd5b506104816109b23660046152de565b61235a565b3480156109c357600080fd5b506105436109d236600461543b565b6123c9565b3480156109e357600080fd5b506109f76109f236600461530a565b6125b7565b6040516104df9291906155b2565b348015610a1157600080fd5b506104d3610a2036600461530a565b612641565b348015610a3157600080fd5b50610543610a403660046152de565b61264e565b348015610a5157600080fd5b50610481610a60366004615327565b61266f565b348015610a7157600080fd5b50610508610a8036600461530a565b612705565b348015610a9157600080fd5b50610543610aa036600461543b565b61272a565b348015610ab157600080fd5b50610481610ac0366004615619565b612780565b348015610ad157600080fd5b50610481610ae03660046156bf565b612a97565b348015610af157600080fd5b5060075461075a906001600160a01b031681565b348015610b1157600080fd5b50610543610b203660046152de565b612ab4565b348015610b3157600080fd5b50610481610b4036600461538a565b612ada565b348015610b5157600080fd5b50610508612b25565b348015610b6657600080fd5b50610543610b75366004615700565b6001600160a01b039283166000908152601360209081526040808320948616835260069094018152838220929094168152925290205490565b348015610bba57600080fd5b50610543610bc93660046152de565b612b46565b348015610bda57600080fd5b50610543610be936600461530a565b612b90565b348015610bfa57600080fd5b50600b5461075a906001600160a01b031681565b348015610c1a57600080fd5b50610481610c29366004615740565b612bbd565b348015610c3a57600080fd5b50610481610c4936600461530a565b612c02565b348015610c5a57600080fd5b50610481610c693660046152de565b612c38565b348015610c7a57600080fd5b50610543610c893660046152de565b612c64565b348015610c9a57600080fd5b50610543610ca9366004615765565b612c7d565b348015610cba57600080fd5b506104d3610cc936600461530a565b612cde565b348015610cda57600080fd5b50610543610ce936600461530a565b612ceb565b610d01610cfc3660046157ad565b612d18565b6040516104df9190615822565b348015610d1a57600080fd5b50610481610d2936600461530a565b612e6d565b348015610d3a57600080fd5b5061054360095481565b348015610d5057600080fd5b50610481610d5f36600461530a565b612ed7565b348015610d7057600080fd5b50610481610d7f366004615884565b612f0c565b348015610d9057600080fd5b50610543610d9f3660046158b7565b6130eb565b348015610db057600080fd5b50610543610dbf366004615765565b613158565b348015610dd057600080fd5b50610481610ddf366004615912565b6131a1565b348015610df057600080fd5b50610481610dff3660046152a5565b6131f5565b348015610e1057600080fd5b50610481610e1f36600461530a565b613259565b348015610e3057600080fd5b50610481610e3f3660046152de565b613300565b348015610e5057600080fd5b5061054360085481565b348015610e6657600080fd5b50610543610e75366004615934565b613459565b348015610e8657600080fd5b50610481610e953660046152de565b6134bb565b348015610ea657600080fd5b50601a5461075a906001600160a01b031681565b348015610ec657600080fd5b50610543610ed536600461543b565b61351c565b348015610ee657600080fd5b50610543610ef53660046152de565b613566565b348015610f0657600080fd5b5060035461075a906001600160a01b031681565b348015610f2657600080fd5b5060025461075a906001600160a01b031681565b348015610f4657600080fd5b50610543610f55366004615740565b6135f8565b610f62613702565b6001600160a01b038216600081815260056020908152604091829020805460ff19168515159081179091558251938452908301527e7a59171ef284e295f019cc84b743f8ef2d67121ae078a0e4668a7f992f6cd891015b60405180910390a15050565b610fcd61372e565b610fd5613755565b610fde82613784565b6001600160a01b0382166000908152601560205260409020610fff836137b7565b600681018054600090915561101581151561389f565b815461010090046001600160a01b03166000611033868430886138bd565b9050600061271060095483611048919061599d565b61105291906159bc565b9050600061106082846159de565b9050600061106e8583611467565b905061107a8982613981565b600a546110929086906001600160a01b031685613a30565b6007546110aa9086906001600160a01b031684613a30565b60075460405163bc04f0af60e01b81526001600160a01b038781166004830152602482018590529091169063bc04f0af90604401600060405180830381600087803b1580156110f857600080fd5b505af115801561110c573d6000803e3d6000fd5b5050604080518b8152602081018890529081018490526001600160a01b038c1692507f4534f107610758c3931de9ad1e176476fcfb8c74adf920167e1d54ee84fcfe76915060600160405180910390a25050505050505061116d6000600155565b5050565b6001600160a01b03811660009081526011602052604081208190819061119681613b4d565b6001820154915490969195509350915050565b60006111b361372e565b6111bb613bc7565b6111c760008411613c58565b6001600160a01b03808516600090815260156020526040902080549091610100909104166111f486613784565b6111fd86613c76565b61120681613cbd565b61120f86613d04565b6000611228600061121f33613d55565b9250821361389f565b600061123888610a408585612ab4565b90506000818811611249578761124b565b815b905060006112598a83612b46565b905060006112698b83308c6138bd565b90506112776000821161389f565b6001600160a01b03861660009081526012602052604081209061129982613b4d565b9050808311156112cb5787838260405163637e01ad60e01b81526004016112c2939291906159f5565b60405180910390fd5b6112d48d6137b7565b6112dd33613ecc565b60006112e98985611467565b90506112f5338f613f34565b611300338f88614004565b61131b3361130d836140a7565b61131690615a16565b6140d1565b6113288e86614107614113565b611332838561424a565b60075461134a908a906001600160a01b031686613a30565b60075460405163bc04f0af60e01b81526001600160a01b038b81166004830152602482018790529091169063bc04f0af90604401600060405180830381600087803b15801561139857600080fd5b505af11580156113ac573d6000803e3d6000fd5b505050506113b933614271565b886001600160a01b03168e6001600160a01b0316336001600160a01b03167f8246cc71ab01533b5bebc672a636df812f10637ad720797319d5741d5ebb39628985604051611411929190918252602082015260400190565b60405180910390a450939850505050505050505061142f6000600155565b9392505050565b6001600160a01b03808316600090815260136020908152604080832093851683526005909301905220545b92915050565b6001600160a01b03821660009081526014602052604081206001015461142f908361599d565b6004546114a4906001600160a01b0316151561389f565b6004546001600160a01b031633146114ce576040516282b42960e81b815260040160405180910390fd5b60048054600380546001600160a01b0383166001600160a01b031991821681179092559091169091556040519081527f54e4612788f90384e6843298d7854436f3a585b2c3831ab66abf1de63bfa6c2d9060200160405180910390a1604051600081527fa728e84b447788a55ff664fbfb5c3983925f88b80b672a1b0b8271c94b22df359060200160405180910390a1565b611568613bc7565b61116d3383836142e3565b61157b613702565b61158f670de0b6b3a7640000821015613c58565b60088190556040518181527f6f428e543b1146a0996303f0250c6fe0682874c690989e63ae53bf75bed8ef6f906020015b60405180910390a150565b6115d3613702565b6115dc8361434a565b6001600160a01b03831660009081526012602052604090206115fd90614355565b6001600160a01b038316600090815260126020526040902061162090838361436c565b60408051838152602081018390526001600160a01b038516917f0f3828e75ac2c1d15158acbdaab952f19d3a532547df5baf02f3fbbde1336d7191015b60405180910390a2505050565b611672613702565b6116f0816001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116d79190615a32565b6001600160a01b0316836001600160a01b03161461389f565b6116f982613784565b6001600160a01b03828116600090815260156020526040902060010180546001600160a01b03191691831691909117905561173782826000196143f5565b6117a5816001600160a01b0316632495a5996040518163ffffffff1660e01b8152600401602060405180830381865afa158015611778573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061179c9190615a32565b826000196143f5565b604080516001600160a01b038085168252831660208201527f0da5485c3be09248db5d9e03e12a4d5ef2df8074685a3dbb6e4e2a6c36f3e4729101610fb9565b6060601860000180548060200260200160405190810160405280929190818152602001828054801561184057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611822575b5050505050905090565b6001600160a01b0382166000908152601560205260408120600701548103611873575080611461565b61187c83614428565b6001600160a01b0384166000908152601560205260409020600701546118a2908461599d565b61142f91906159bc565b6118b46144b2565b6118bd82613784565b6001600160a01b038216600081815260156020908152604091829020600d01805460ff191685151590811790915591519182527fb15476211644e7e7a92dd4fe21d831367f5ea58130817ad39712dff67139989691015b60405180910390a25050565b611928613702565b61193460008211613c58565b61193d82613784565b61194f81670de0b6b3a76400006159bc565b6001600160a01b0383166000818152601560209081526040918290206004019390935580519182529181018390527fd5710c9f352f135b893ded82d4669bd05e5b1a7ad8d615e2ae88bc732673ad7d9101610fb9565b6001600160a01b03811660009081526013602052604081208190600301815b8154811015611a645760008260000182815481106119e4576119e4615a4f565b60009182526020808320909101546001600160a01b03908116808452601583526040808520548b84168652601385528186208387526001019094528420549094506101009092041691611a378483612c64565b9050611a438382611467565b611a4d9088615a65565b96505050505080611a5d90615a7d565b90506119c4565b50909392505050565b611a7561372e565b611a7d613702565b611a8f61271082602001351115613c58565b611a9f6000826060013511613c58565b611ab3611aad6018846144f5565b1561389f565b6000611ac2602083018361530a565b9050611b42816001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b299190615a32565b6001600160a01b0316846001600160a01b03161461389f565b611bac816001600160a01b0316632495a5996040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba79190615a32565b61434a565b604051806101e00160405280611bc185614516565b60ff168152602001826001600160a01b0316632495a5996040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c2b9190615a32565b6001600160a01b03168152602090810190611c489085018561530a565b6001600160a01b0316815260200183602001358152602001836040013581526020018360600135670de0b6b3a7640000611c8291906159bc565b81526000602080830182905260408084018390526060808501849052608080860185905260a080870186905260c080880187905260e0808901889052610100808a01899052610120998a018990526001600160a01b038e81168a5260158952988790208b518154998d015160ff9091166001600160a81b0319909a1699909917988a168202989098178855958a01516001880180546001600160a01b0319169190991617909755928801516002860155908701516003850155860151600484015585015160058301559184015160068201559083015160078201559082015160088201556101408201516009820155610160820151600a820155610180820151600b8201556101a0820151600c8201556101c090910151600d909101805460ff1916911515919091179055611db86018846145fc565b50611dd283611dca602085018561530a565b6000196143f5565b611e44816001600160a01b0316632495a5996040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e379190615a32565b611dca602085018561530a565b6040516001600160a01b038416907f7d45e47bed6d03b67d9ccb28bed226dd01d1425049e21b289d6fd54732131c8390600090a27f0da5485c3be09248db5d9e03e12a4d5ef2df8074685a3dbb6e4e2a6c36f3e47283611ea7602085018561530a565b604080516001600160a01b0393841681529290911660208301520160405180910390a1826001600160a01b03167f75bfe099d672835dbb3c09443e8493c7f306b46d695f749eb0bcf7352bef01478360200135604051611f0991815260200190565b60405180910390a25061116d6000600155565b611f246144b2565b611f2d8261434a565b6001600160a01b038216600081815260146020908152604091829020600201805460ff191685151590811790915591519182527fac216db82ec84df77384646fb0bd5f9bfd21ac8c46cfcf3a27a0d2feede572e29101611914565b611f90613702565b611fa46001600160a01b0382161515613c58565b600a80546001600160a01b0319166001600160a01b0383169081179091556040519081527f76203fab169061f503a4139455a7dfc20cc2d5a59c97d5bb810e431f4059aad7906020016115c0565b6001600160a01b03808316600090815260156020526040812060018101548154929391929116906120279060ff16600a615b7a565b816001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612065573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120899190615b89565b612093908661599d565b61209d91906159bc565b95945050505050565b60606016600001805480602002602001604051908101604052809291908181526020018280548015611840576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311611822575050505050905090565b6001600160a01b03808316600090815260136020908152604080832093851683526001840182528083205460029094019091529020545b9250929050565b61214f613702565b600480546001600160a01b0319166001600160a01b0383169081179091556040519081527fa728e84b447788a55ff664fbfb5c3983925f88b80b672a1b0b8271c94b22df35906020016115c0565b6121a561372e565b6121ad613bc7565b6121b960008311613c58565b6121cd6001600160a01b0382161515613c58565b6121d883338461465d565b6121e38383836146a2565b6121ed6000600155565b505050565b612282604051806101e00160405280600060ff16815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000151581525090565b506001600160a01b0390811660009081526015602090815260409182902082516101e081018452815460ff8082168352610100918290048716948301949094526001830154909516938101939093526002810154606084015260038101546080840152600481015460a0840152600581015460c0840152600681015460e084015260078101549383019390935260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d9092015490911615156101c082015290565b612362613702565b612370612710821115613c58565b61237982613784565b6001600160a01b03821660008181526015602052604090819020600201839055517f75bfe099d672835dbb3c09443e8493c7f306b46d695f749eb0bcf7352bef0147906119149084815260200190565b60006123d361372e565b6123db613bc7565b6123e760008411613c58565b6123fb6001600160a01b0383161515613c58565b6124048461434a565b61240d84613cbd565b61241682613ecc565b61241f82614740565b6001600160a01b0382166000908152601360205260408120549061244490821361389f565b60006124508683612ab4565b905060008186116124615785612463565b815b6001600160a01b038816600090815260116020526040812091925061248782613b4d565b9050808311156124b0578883826040516395e6194b60e01b81526004016112c2939291906159f5565b60006124bc8a85611467565b90506124cb8861130d836140a7565b6124d5838561424a565b6007546124ef908b9033906001600160a01b0316876147a9565b60075460405163bc04f0af60e01b81526001600160a01b038c81166004830152602482018790529091169063bc04f0af90604401600060405180830381600087803b15801561253d57600080fd5b505af1158015612551573d6000803e3d6000fd5b5050604080518781526001600160a01b038c81166020830152918101859052908d1692503391507f7b56e0a28bd3c28702bc74e47867c0c083a31a2b90952ed60a91c4fbd5a0e9289060600160405180910390a3509194505050505061142f6000600155565b6001600160a01b03811660009081526013602052604081206060906125db84613d55565b6003820180546040805160208084028201810190925282815291839183018282801561263057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612612575b505050505090509250925050915091565b60006114616016836144f5565b60008061265b8484613566565b9050612667848261184a565b949350505050565b612677613702565b6126808361434a565b6001600160a01b03831660009081526011602052604090206126a190614355565b6001600160a01b03831660009081526011602052604090206126c490838361436c565b60408051838152602081018390526001600160a01b038516917f86d00f048da605f1d0b35d86d59306a26293721713d9d41eef94518316eb1834910161165d565b6001600160a01b03811660009081526012602052604081208190819061119681613b4d565b600061273461372e565b61273c613bc7565b6127506001600160a01b0383161515613c58565b61275984613784565b6000612767853386866148be565b9050612774858483613a30565b905061142f6000600155565b600054610100900460ff16158080156127a05750600054600160ff909116105b806127ba5750303b1580156127ba575060005460ff166001145b61281d5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016112c2565b6000805460ff191660011790558015612840576000805461ff0019166101001790555b61285261271083608001511115613c58565b6020820151600280546001600160a01b03199081166001600160a01b0393841617909155835160038054831691841691909117905560408401516007805483169184169190911790556060840151600855608084015160095560a0840151600a80548316918416919091179055610120840151600b8054909216921691909117905560e082015161010083015160c08401516128ef92919061495d565b8051600c55602080820151600d55604080830151600e556060830151600f5560809092015160105560035491516001600160a01b0390921682527f54e4612788f90384e6843298d7854436f3a585b2c3831ab66abf1de63bfa6c2d910160405180910390a16007546040516001600160a01b0390911681527fe42bb1780117f5788c6e0042e955291223819875891ddf465b8e4fbb85a933e99060200160405180910390a17f6f428e543b1146a0996303f0250c6fe0682874c690989e63ae53bf75bed8ef6f6008546040516129c791815260200190565b60405180910390a17fd10d75876659a287a59a6ccfa2e3fff42f84d94b542837acd30bc184d562de40600954604051612a0291815260200190565b60405180910390a17f8353d6f6057b53994c31faf6112674bd84b37caab3af81c269080ae9904ebbeb8260e00151836101000151604051612a4d929190918252602082015260400190565b60405180910390a1801561116d576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001610fb9565b612a9f613bc7565b612aa882613784565b6121ed33848484614a21565b6001600160a01b03821660009081526014602052604081206001015461142f90836159bc565b612ae2613702565b612af0612710821115613c58565b60098190556040518181527fd10d75876659a287a59a6ccfa2e3fff42f84d94b542837acd30bc184d562de40906020016115c0565b6000806000612b34600c613b4d565b600d54600c5491959094509092509050565b6001600160a01b038216600090815260156020526040812060070154808203612b725782915050611461565b80612b7c85614428565b612b86908561599d565b61266791906159bc565b6001600160a01b038116600090815260156020526040812054611461908390610c899060ff16600a615b7a565b612bc561372e565b612bcd613bc7565b612bd960008311613c58565b612bed6001600160a01b0382161515613c58565b612bf83383836146a2565b61116d6000600155565b612c0a61372e565b612c12613702565b601a80546001600160a01b0319166001600160a01b038316179055600060015550565b50565b612c4061372e565b612c48614a97565b612c5182614740565b612c5b82826140d1565b612bf882614271565b600080612c718484612b46565b90506126678482611ff2565b6000612c8761372e565b612c8f613bc7565b612ca36001600160a01b0384161515613c58565b612cac85613784565b612cb585613d04565b6000612cc3863387876148be565b9050612cd1868286866138bd565b9150506126676000600155565b60006114616018836144f5565b6001600160a01b038116600090815260156020526040812054611461908390610bc99060ff16600a615b7a565b60608167ffffffffffffffff811115612d3357612d336155cb565b604051908082528060200260200182016040528015612d6657816020015b6060815260200190600190039081612d515790505b50905060005b82811015612e665760008030868685818110612d8a57612d8a615a4f565b9050602002810190612d9c9190615ba2565b604051612daa929190615be9565b600060405180830381855af49150503d8060008114612de5576040519150601f19603f3d011682016040523d82523d6000602084013e612dea565b606091505b509150915081612e3557858584818110612e0657612e06615a4f565b9050602002810190612e189190615ba2565b8260405163070c497560e21b81526004016112c293929190615bf9565b80848481518110612e4857612e48615a4f565b6020026020010181905250505080612e5f90615a7d565b9050612d6c565b5092915050565b612e75613702565b612e896001600160a01b0382161515613c58565b600780546001600160a01b0319166001600160a01b0383169081179091556040519081527fe42bb1780117f5788c6e0042e955291223819875891ddf465b8e4fbb85a933e9906020016115c0565b612edf61372e565b612ee7613bc7565b612ef081614ac1565b612ef981613ecc565b612f0281614740565b612c356000600155565b612f1461372e565b612f1c613702565b612f2a611aad6016846144f5565b6000612f3583614516565b600254909150600090612f50906001600160a01b0316614516565b9050612f648160ff168360ff161115613c58565b60405180606001604052808360ff1681526020018383612f849190615c37565b612f8f90600a615b7a565b8152600060209182018190526001600160a01b0387168152601482526040908190208351815460ff90911660ff1991821617825584840151600183015593820151600290910180549115159190941617909255612ff5919085013590850135853561495d565b6001600160a01b038516600090815260116020908152604091829020835181559083015160018201559082015160028201556060808301516003830155608092830151600490920191909155613057918501359060a08601359086013561495d565b6001600160a01b03851660009081526012602090815260409182902083518155908301516001820155908201516002820155606082015160038201556080909101516004909101556130aa6016856145fc565b506040516001600160a01b038516907f3a0e2b79f1e0c4867b78173318e64bad2dbf7be510d51198d37900e52de2bb8c90600090a2505061116d6000600155565b60006130f561372e565b6130fd613bc7565b6131116001600160a01b0384161515613c58565b61311a85613784565b61312385613d04565b61312f86338787614b29565b600061313d868887876148be565b905061314b868286866138bd565b91505061209d6000600155565b600061316261372e565b61316a613bc7565b61317e6001600160a01b0384161515613c58565b61318785613784565b6000613194868685614b6e565b9050612cd1868286614c49565b6131a9613702565b6131b3600c614355565b6131bf600c838361436c565b60408051838152602081018390527f8353d6f6057b53994c31faf6112674bd84b37caab3af81c269080ae9904ebbeb9101610fb9565b6131fd613702565b6001600160a01b038216600081815260066020908152604091829020805460ff19168515159081179091558251938452908301527f8dd62d4e1f60b96148552898e743aa2b571686baa26f4f1b647565dc3996c1a79101610fb9565b61326161372e565b613269613702565b61327281613784565b6001600160a01b038116600090815260156020526040812060050154613299908390611ff2565b6001600160a01b0383166000818152601560205260409081902060080183905551919250907fccda4b864ea094cd2323d3a440088f1171a7c3e5c46d87f6baafabe470d614da906132ed9084815260200190565b60405180910390a250612c356000600155565b61330861372e565b613310613bc7565b61331c60008211613c58565b61332582614d50565b61332e33614740565b3360009081526013602090815260408083206001600160a01b0386168452600101825280832054601590925282206007015461336a91906159de565b90508061337f670de0b6b3a76400008461599d565b61338991906159bc565b6001600160a01b0384166000908152601560205260408120600c0180549091906133b4908490615a65565b90915550506001600160a01b038084166000818152601560209081526040808320600c015433808552601384528285209585526002958601909352922091909155905461340392169084614dd0565b61340e600c83614e03565b6040518281526001600160a01b0384169033907f88dcaca629d63d86330e97adc358b13dd0ebd703239aea96b7ea2fb331b16f4e9060200160405180910390a35061116d6000600155565b600061346361372e565b61346b613bc7565b61347f6001600160a01b0383161515613c58565b61348884613784565b61349485338686614b29565b60006134a2858786866148be565b90506134af858483613a30565b90506126676000600155565b6134c3613702565b6134cc82613784565b6001600160a01b03821660008181526015602052604090819020600301839055517f4bd29da0a205336da3aaf81eada8ef63426a1c923b4a434e54ac7f898fa53c9a906119149084815260200190565b600061352661372e565b61352e613bc7565b6135426001600160a01b0383161515613c58565b61354b84613784565b6000613558858585614c49565b9050612774853330876147a9565b6001600160a01b0380831660009081526015602090815260408083206001810154825163501ad8ff60e11b8152925194959194911692839263a035b1fe92600480830193928290030181865afa1580156135c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135e89190615b89565b82546120899060ff16600a615b7a565b600061360261372e565b61360a613bc7565b61361660008411613c58565b61362a6001600160a01b0383161515613c58565b61363382613ecc565b61363c82614740565b6001600160a01b0382166000908152601360205260408120549061366190821361389f565b60008185116136705784613672565b815b90506136818461130d836140a7565b600254613698906001600160a01b03163383614dd0565b6136a3600c82614e03565b604080518281526001600160a01b038616602082015233917fdbdf9b8e4b75e75b162d151ec8fc7f0561cabab5fcccfa2600be62223e4300c4910160405180910390a29150506114616000600155565b6001600160a01b03163b151590565b6003546001600160a01b0316331461372c576040516282b42960e81b815260040160405180910390fd5b565b6001541561374f57604051632ecfcaf960e01b815260040160405180910390fd5b60018055565b3360009081526006602052604090205460ff1661372c576040516282b42960e81b815260040160405180910390fd5b61378f6018826144f5565b612c3557604051635f8b555b60e11b81526001600160a01b03821660048201526024016112c2565b6001600160a01b038116600090815260156020526040812060050154908190036137df575050565b60006137eb8383611ff2565b6001600160a01b0384166000908152601560205260409020600801549091508082116138175750505050565b600061382785610ef584866159de565b905080600003613838575050505050565b6001600160a01b038516600090815260156020526040812060050180548392906138639084906159de565b90915550506001600160a01b03851660009081526015602052604081206006018054839290613893908490615a65565b90915550505050505050565b80612c3557604051634a613c4160e01b815260040160405180910390fd5b6001600160a01b03848116600090815260156020526040808220600101549051637647691d60e01b81526004810187905285841660248201529192169082908290637647691d906044016020604051808303816000875af1158015613926573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061394a9190615b89565b905083811015613977576040516371c4efed60e01b815260048101829052602481018590526044016112c2565b9695505050505050565b6001600160a01b03821660009081526015602052604081206009810154600a82015491929091906139b186614e1a565b905060006139bf8284615a65565b6139c990856159de565b90508115613a0b5760078501546139e8670de0b6b3a76400008461599d565b6139f291906159bc565b85600c016000828254613a059190615a65565b90915550505b613a158187615a65565b600986015550506000600a840155505043600b909101555050565b6040516001600160a01b03838116602483015260448201839052600091829186169063a9059cbb60e01b906064015b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051613a9d9190615c5a565b6000604051808303816000865af19150503d8060008114613ada576040519150601f19603f3d011682016040523d82523d6000602084013e613adf565b606091505b5091509150846001600160a01b03163b60001480613afb575081155b80613b225750805115801590613b22575080806020019051810190613b209190615c76565b155b15613b465784828260405163e7e40b5b60e01b81526004016112c293929190615c93565b5050505050565b600080826003015443613b6091906159de565b905080600003613b735750506002015490565b6000670de0b6b3a7640000846001015483613b8e919061599d565b613b9891906159bc565b90506000818560020154613bac9190615a65565b85549091508111613bbd578061209d565b5050915492915050565b333203613bd057565b600b54604051633af32abf60e01b81523360048201526001600160a01b0390911690633af32abf90602401602060405180830381865afa158015613c18573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c3c9190615c76565b61372c576040516282b42960e81b815260040160405180910390fd5b80612c3557604051630134249960e71b815260040160405180910390fd5b6001600160a01b0381166000908152601560205260409020600d015460ff16612c35576040516303f585af60e31b81526001600160a01b03821660048201526024016112c2565b6001600160a01b03811660009081526014602052604090206002015460ff16612c35576040516303f585af60e31b81526001600160a01b03821660048201526024016112c2565b6000613d0f82614ec2565b6001600160a01b038316600090815260156020526040902060020154909150808211156121ed57828282604051635c1bc28160e01b81526004016112c2939291906159f5565b6001600160a01b0381166000908152601360205260408120805490600301825b8154811015611a64576000826000018281548110613d9557613d95615a4f565b60009182526020808320909101546001600160a01b03908116808452601583526040808520600c0154928b168552601384528085208286526002019093529183205491935091613de484614e1a565b905060008111613df5576000613e2d565b6001600160a01b038416600090815260156020526040902060070154613e23670de0b6b3a76400008361599d565b613e2d91906159bc565b613e379084615a65565b9250818303613e495750505050613ebc565b6001600160a01b03808a1660009081526013602090815260408083209388168352600190930190529081205490670de0b6b3a764000082613e8a86886159de565b613e94919061599d565b613e9e91906159bc565b9050613ea9816140a7565b613eb3908a615cbf565b98505050505050505b613ec581615a7d565b9050613d75565b6001600160a01b0381166000908152601360205260408120600301905b81548110156121ed57613f24826000018281548110613f0a57613f0a615a4f565b6000918252602090912001546001600160a01b0316614d50565b613f2d81615a7d565b9050613ee9565b6001600160a01b038083166000908152601360209081526040808320938516835260158252808320600c01546002850190925290912054808203613f79575050505050565b6001600160a01b038416600090815260018401602052604081205490670de0b6b3a764000082613fa985876159de565b613fb3919061599d565b613fbd91906159bc565b9050613fc8816140a7565b856000016000828254613fdb9190615cbf565b9091555050506001600160a01b0390941660009081526002909301602052506040909120555050565b6001600160a01b0380841660009081526013602090815260408083209386168352600184019091528120805484929061403e9084906159de565b90915550506001600160a01b0383166000908152601560205260408120600701805484929061406e9084906159de565b90915550506001600160a01b038316600090815260018201602052604081205490036140a157613b466003820184614fd3565b50505050565b6000600160ff1b82106140cd57604051630134249960e71b815260040160405180910390fd5b5090565b6001600160a01b03821660009081526013602052604081208054909183918391906140fd908490615cfe565b9091555050505050565b600061142f82846159de565b6001600160a01b03808416600090815260156020908152604080832081516101e081018352815460ff8082168352610100918290048816958301959095526001830154909616928101929092526002810154606083015260038101546080830152600481015460a0830152600581015460c0830152600681015460e083015260078101549482019490945260088401546101208201526009840154610140820152600a840154610160820152600b840154610180820152600c8401546101a0820152600d909301541615156101c08301526141ee8585611ff2565b905060006142048360c00151868663ffffffff16565b9050600061421b846101200151848763ffffffff16565b6001600160a01b0390971660009081526015602052604090206005810192909255506008019490945550505050565b600061425583613b4d565b905061426182826159de565b6002840155505043600390910155565b6001600160a01b038116600090815260136020526040812054908113614295575050565b600081670de0b6b3a76400006142aa856119a5565b6142b4919061599d565b6142be91906159bc565b90506008548110156121ed5760405163fddafdf560e01b815260040160405180910390fd5b6001600160a01b03838116600081815260136020908152604080832094871680845260058601835292819020869055518581529192917f09cd9a2cf8d0dd3936aef63dae78b3085d8abaaec9311145115b15e5e3eaea41910160405180910390a350505050565b61378f6016826144f5565b61435e81613b4d565b600282015543600390910155565b611c2081111561438f57604051630134249960e71b815260040160405180910390fd5b82600401548210156143b457604051630134249960e71b815260040160405180910390fd5b81836002015411156143c857600283018290555b818355806143de670de0b6b3a76400008461599d565b6143e891906159bc565b8360010181905550505050565b6040516001600160a01b03838116602483015260448201839052600091829186169063095ea7b360e01b90606401613a5f565b6001600160a01b03811660009081526015602052604081206005810154808303614453579392505050565b600061445f8583611ff2565b6008840154909150808211614478575090949350505050565b600061448887610ef584866159de565b90508060000361449d57509195945050505050565b6144a781856159de565b979650505050505050565b6003546001600160a01b031633036144c657565b3360009081526005602052604090205460ff1661372c576040516282b42960e81b815260040160405180910390fd5b6001600160a01b031660009081526001919091016020526040902054151590565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b1790529051600091829182916001600160a01b0386169161455c9190615c5a565b600060405180830381855afa9150503d8060008114614597576040519150601f19603f3d011682016040523d82523d6000602084013e61459c565b606091505b5091509150836001600160a01b03163b600014806145b8575081155b806145c4575060208151105b156145e85783828260405163e7e40b5b60e01b81526004016112c293929190615c93565b808060200190518101906126679190615d3f565b600061460883836144f5565b1561461557506000611461565b508154600180820184556000848152602080822090930180546001600160a01b0319166001600160a01b03959095169485179055845493815293810190915260409092205590565b6001600160a01b038084166000908152601360209081526040808320938616835260058401909152812080548492906146979084906159de565b909155505050505050565b6146ab82615119565b6146b483614ac1565b6146bd83613ecc565b6146c683614740565b6146d383611316846140a7565b6146dc83614271565b6146e7600c8361424a565b6002546146fe906001600160a01b03168284615152565b604080518381526001600160a01b0383811660208301528516917fbcad3d7d3dfccb90d49c6063bf70f828901fefc88937d90af74e58e6e55bc39d910161165d565b6001600160a01b0381166000908152601360205260408120600301905b81548110156121ed576147998383600001838154811061477f5761477f615a4f565b6000918252602090912001546001600160a01b0316613f34565b6147a281615a7d565b905061475d565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b179052915160009283929088169161480d9190615c5a565b6000604051808303816000865af19150503d806000811461484a576040519150601f19603f3d011682016040523d82523d6000602084013e61484f565b606091505b5091509150856001600160a01b03163b6000148061486b575081155b8061489257508051158015906148925750808060200190518101906148909190615c76565b155b156148b65785828260405163e7e40b5b60e01b81526004016112c293929190615c93565b505050505050565b60006148c984614ac1565b6148d284613ecc565b60006148de8685612b46565b90506148e985614740565b6148f4858786614004565b6149018682614107614113565b61490a85614271565b604080518581526001600160a01b03858116602083015280891692908816917f3ed4ee04a905a278b050a856bbe7ddaaf327a30514373e65aa6103beeae488c3910160405180910390a395945050505050565b61498f6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b611c208311156149b257604051630134249960e71b815260040160405180910390fd5b818410156149d357604051630134249960e71b815260040160405180910390fd5b6040518060a0016040528085815260200184670de0b6b3a7640000876149f9919061599d565b614a0391906159bc565b81526020018581526020014381526020018381525090509392505050565b6001600160a01b038481166000818152601360209081526040808320888616808552600682018452828520968916808652968452938290208790559051868152909493917f14d43ec44802545adffd2e9b5d923acf7ae9106d7774f3fd01a3af424e0f262e910160405180910390a45050505050565b601a546001600160a01b0316331461372c576040516282b42960e81b815260040160405180910390fd5b6001600160a01b0381166000908152601360205260408120600301905b81548110156121ed57614b19826000018281548110614aff57614aff615a4f565b6000918252602090912001546001600160a01b03166137b7565b614b2281615a7d565b9050614ade565b6001600160a01b0380851660009081526013602090815260408083208785168452600681018352818420948716845293909152812080548492906138939084906159de565b6001600160a01b03808416600090815260156020526040812060018101548154929391929082169161010090910416614ba9813330896147a9565b604051629dd64160e51b8152600481018790523060248201526000906001600160a01b038416906313bac820906044016020604051808303816000875af1158015614bf8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c1c9190615b89565b9050858110156144a7576040516371c4efed60e01b815260048101829052602481018790526044016112c2565b6000614c5760008411613c58565b6001600160a01b0380851660009081526015602052604090208054909161010090910416614c8486613c76565b614c8d81613cbd565b614c9686613d04565b614c9f866137b7565b614ca884613ecc565b614cb28487613f34565b6000614cbf858888615185565b9050614cce8787615266614113565b60038301546008840154811015614cfe5787878260405163a3528cf560e01b81526004016112c2939291906159f5565b604080518881526001600160a01b0388811660208301528a169133917f364bb76a44233df8584c690de6da7810626a5e77192f3ebc942c35bcb1add24f910160405180910390a3509695505050505050565b6001600160a01b038116600090815260156020526040812090614d7283614e1a565b905080600003614d8157505050565b6007820154614d98670de0b6b3a76400008361599d565b614da291906159bc565b82600c016000828254614db59190615a65565b925050819055508082600a0160008282546140fd9190615a65565b6040516001600160a01b03838116602483015260448201839052600091829186169063079cc67960e41b90606401613a5f565b6000614e0e83613b4d565b90506142618282615a65565b6001600160a01b03811660009081526015602052604081206009810154808303614e48575060009392505050565b6004820154600a830154600b840154600083614e6483436159de565b614e6e919061599d565b9050670de0b6b3a76400008110614e8e57614e8983866159de565b614eb6565b82670de0b6b3a7640000614ea2838861599d565b614eac91906159bc565b614eb691906159de565b98975050505050505050565b6001600160a01b03808216600090815260156020908152604080832081516101e081018352815460ff8082168352610100918290048816958301959095526001830154909616928101929092526002810154606083015260038101546080830152600481015460a0830152600581015460c08301819052600682015460e084015260078201549583019590955260088101546101208301526009810154610140830152600a810154610160830152600b810154610180830152600c8101546101a0830152600d015490911615156101c082015290918290614fa4908590611ff2565b610120830151909150818111614fbb57600061209d565b80612710614fc984836159de565b612093919061599d565b6001600160a01b0381166000908152600183016020526040812054808203614fff576000915050611461565b8061500981615d62565b85549092506000915061501e906001906159de565b90508082146150be57600085600001828154811061503e5761503e615a4f565b60009182526020909120015486546001600160a01b039091169150819087908590811061506d5761506d615a4f565b600091825260209091200180546001600160a01b0319166001600160a01b03929092169190911790556150a1836001615a65565b6001600160a01b0390911660009081526001870160205260409020555b84548590806150cf576150cf615d79565b60008281526020808220600019908401810180546001600160a01b03191690559092019092556001600160a01b039590951681526001958601909452505060408220919091555090565b6000615125600c613b4d565b90508082111561116d576040516313d73d6b60e01b815260048101839052602481018290526044016112c2565b6040516001600160a01b0383811660248301526044820183905260009182918616906340c10f1960e01b90606401613a5f565b600080615192848461184a565b6001600160a01b03808716600090815260136020908152604080832093891683526001909301905290812054919250036151ed576001600160a01b03851660009081526013602052604090206151eb90600301856145fc565b505b6001600160a01b0380861660009081526013602090815260408083209388168352600190930190529081208054839290615228908490615a65565b90915550506001600160a01b03841660009081526015602052604081206007018054839290615258908490615a65565b909155509095945050505050565b600061142f8284615a65565b6001600160a01b0381168114612c3557600080fd5b803561529281615272565b919050565b8015158114612c3557600080fd5b600080604083850312156152b857600080fd5b82356152c381615272565b915060208301356152d381615297565b809150509250929050565b600080604083850312156152f157600080fd5b82356152fc81615272565b946020939093013593505050565b60006020828403121561531c57600080fd5b813561142f81615272565b60008060006060848603121561533c57600080fd5b833561534781615272565b95602085013595506040909401359392505050565b6000806040838503121561536f57600080fd5b823561537a81615272565b915060208301356152d381615272565b60006020828403121561539c57600080fd5b5035919050565b600081518084526020808501945080840160005b838110156153dc5781516001600160a01b0316875295820195908201906001016153b7565b509495945050505050565b60208152600061142f60208301846153a3565b60008082840360a081121561540e57600080fd5b833561541981615272565b92506080601f198201121561542d57600080fd5b506020830190509250929050565b60008060006060848603121561545057600080fd5b833561545b81615272565b925060208401359150604084013561547281615272565b809150509250925092565b60005b83811015615498578181015183820152602001615480565b838111156140a15750506000910152565b600081518084526154c181602086016020860161547d565b601f01601f19169290920160200192915050565b60208152600061142f60208301846154a9565b815160ff1681526101e08101602083015161550e60208401826001600160a01b03169052565b50604083015161552960408401826001600160a01b03169052565b50606083810151908301526080808401519083015260a0808401519083015260c0808401519083015260e08084015190830152610100808401519083015261012080840151908301526101408084015190830152610160808401519083015261018080840151908301526101a080840151908301526101c0928301511515929091019190915290565b82815260406020820152600061266760408301846153a3565b634e487b7160e01b600052604160045260246000fd5b604051610140810167ffffffffffffffff8111828210171561561357634e487b7160e01b600052604160045260246000fd5b60405290565b6000610140828403121561562c57600080fd5b6156346155e1565b61563d83615287565b815261564b60208401615287565b602082015261565c60408401615287565b6040820152606083013560608201526080830135608082015261568160a08401615287565b60a082015260c083013560c082015260e083013560e08201526101008084013581830152506101206156b4818501615287565b908201529392505050565b6000806000606084860312156156d457600080fd5b83356156df81615272565b925060208401356156ef81615272565b929592945050506040919091013590565b60008060006060848603121561571557600080fd5b833561572081615272565b9250602084013561573081615272565b9150604084013561547281615272565b6000806040838503121561575357600080fd5b8235915060208301356152d381615272565b6000806000806080858703121561577b57600080fd5b843561578681615272565b935060208501359250604085013561579d81615272565b9396929550929360600135925050565b600080602083850312156157c057600080fd5b823567ffffffffffffffff808211156157d857600080fd5b818501915085601f8301126157ec57600080fd5b8135818111156157fb57600080fd5b8660208260051b850101111561581057600080fd5b60209290920196919550909350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561587757603f198886030184526158658583516154a9565b94509285019290850190600101615849565b5092979650505050505050565b60008082840360e081121561589857600080fd5b83356158a381615272565b925060c0601f198201121561542d57600080fd5b600080600080600060a086880312156158cf57600080fd5b85356158da81615272565b945060208601356158ea81615272565b935060408601359250606086013561590181615272565b949793965091946080013592915050565b6000806040838503121561592557600080fd5b50508035926020909101359150565b6000806000806080858703121561594a57600080fd5b843561595581615272565b9350602085013561596581615272565b925060408501359150606085013561597c81615272565b939692955090935050565b634e487b7160e01b600052601160045260246000fd5b60008160001904831182151516156159b7576159b7615987565b500290565b6000826159d957634e487b7160e01b600052601260045260246000fd5b500490565b6000828210156159f0576159f0615987565b500390565b6001600160a01b039390931683526020830191909152604082015260600190565b6000600160ff1b8201615a2b57615a2b615987565b5060000390565b600060208284031215615a4457600080fd5b815161142f81615272565b634e487b7160e01b600052603260045260246000fd5b60008219821115615a7857615a78615987565b500190565b600060018201615a8f57615a8f615987565b5060010190565b600181815b80851115615ad1578160001904821115615ab757615ab7615987565b80851615615ac457918102915b93841c9390800290615a9b565b509250929050565b600082615ae857506001611461565b81615af557506000611461565b8160018114615b0b5760028114615b1557615b31565b6001915050611461565b60ff841115615b2657615b26615987565b50506001821b611461565b5060208310610133831016604e8410600b8410161715615b54575081810a611461565b615b5e8383615a96565b8060001904821115615b7257615b72615987565b029392505050565b600061142f60ff841683615ad9565b600060208284031215615b9b57600080fd5b5051919050565b6000808335601e19843603018112615bb957600080fd5b83018035915067ffffffffffffffff821115615bd457600080fd5b60200191503681900382131561214057600080fd5b8183823760009101908152919050565b60408152826040820152828460608301376000606084830101526000601f19601f8501168201606083820301602084015261397760608201856154a9565b600060ff821660ff841680821015615c5157615c51615987565b90039392505050565b60008251615c6c81846020870161547d565b9190910192915050565b600060208284031215615c8857600080fd5b815161142f81615297565b6001600160a01b0384168152821515602082015260606040820181905260009061209d908301846154a9565b60008083128015600160ff1b850184121615615cdd57615cdd615987565b6001600160ff1b0384018313811615615cf857615cf8615987565b50500390565b600080821280156001600160ff1b0384900385131615615d2057615d20615987565b600160ff1b8390038412811615615d3957615d39615987565b50500190565b600060208284031215615d5157600080fd5b815160ff8116811461142f57600080fd5b600081615d7157615d71615987565b506000190190565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220910f37758d901447b5ac7058ca37f362fad29d46491def08d4193ef8b7679e8764736f6c634300080d0033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.