More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 133 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Redeem | 22353648 | 23 days ago | IN | 0 ETH | 0.00164492 | ||||
Redeem | 22352157 | 24 days ago | IN | 0 ETH | 0.00037797 | ||||
Deposit | 22352104 | 24 days ago | IN | 0 ETH | 0.0005076 | ||||
Redeem | 22332047 | 26 days ago | IN | 0 ETH | 0.0009924 | ||||
Rebalance | 22139350 | 53 days ago | IN | 0 ETH | 0.00088542 | ||||
Redeem | 22084574 | 61 days ago | IN | 0 ETH | 0.00034479 | ||||
Redeem | 22041696 | 67 days ago | IN | 0 ETH | 0.0001755 | ||||
Redeem | 22038716 | 67 days ago | IN | 0 ETH | 0.00088705 | ||||
Deposit | 22029977 | 69 days ago | IN | 0 ETH | 0.00040668 | ||||
Redeem | 21996318 | 73 days ago | IN | 0 ETH | 0.00059847 | ||||
Deposit | 21992722 | 74 days ago | IN | 0 ETH | 0.00039946 | ||||
Redeem | 21992342 | 74 days ago | IN | 0 ETH | 0.00022766 | ||||
Deposit | 21974664 | 76 days ago | IN | 0 ETH | 0.00095617 | ||||
Redeem | 21974645 | 76 days ago | IN | 0 ETH | 0.00096979 | ||||
Deposit | 21974634 | 76 days ago | IN | 0 ETH | 0.0010368 | ||||
Redeem | 21971343 | 77 days ago | IN | 0 ETH | 0.00103849 | ||||
Redeem | 21969052 | 77 days ago | IN | 0 ETH | 0.00070175 | ||||
Deposit | 21954196 | 79 days ago | IN | 0 ETH | 0.00028747 | ||||
Deposit | 21947576 | 80 days ago | IN | 0 ETH | 0.0002606 | ||||
Redeem | 21940868 | 81 days ago | IN | 0 ETH | 0.00024811 | ||||
Deposit | 21918218 | 84 days ago | IN | 0 ETH | 0.00026448 | ||||
Deposit | 21911090 | 85 days ago | IN | 0 ETH | 0.00037306 | ||||
Deposit | 21840912 | 95 days ago | IN | 0 ETH | 0.00045194 | ||||
Rebalance | 21724674 | 111 days ago | IN | 0 ETH | 0.00567601 | ||||
Deposit | 21402007 | 156 days ago | IN | 0 ETH | 0.00338361 |
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:
scUSDCv2
Compiler Version
v0.8.21+commit.d9974bed
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; import {NoProfitsToSell, FlashLoanAmountZero, EndUsdcBalanceTooLow, FloatBalanceTooLow} from "../errors/scErrors.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; import {WETH} from "solmate/tokens/WETH.sol"; import {ERC4626} from "solmate/mixins/ERC4626.sol"; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; import {Address} from "openzeppelin-contracts/utils/Address.sol"; import {EnumerableMap} from "openzeppelin-contracts/utils/structs/EnumerableMap.sol"; import {Constants as C} from "../lib/Constants.sol"; import {BaseV2Vault} from "./BaseV2Vault.sol"; import {AggregatorV3Interface} from "../interfaces/chainlink/AggregatorV3Interface.sol"; import {IAdapter} from "./IAdapter.sol"; import {PriceConverter} from "./PriceConverter.sol"; import {Swapper} from "./Swapper.sol"; /** * @title Sandclock USDC Vault version 2 * @notice A vault that allows users to earn interest on their USDC deposits from leveraged WETH staking. * @notice The v2 vault uses multiple lending markets to earn yield on USDC deposits and borrow WETH to stake. * @dev This vault uses Sandclock's leveraged WETH staking vault - scWETH. */ contract scUSDCv2 is BaseV2Vault { using SafeTransferLib for ERC20; using SafeTransferLib for WETH; using FixedPointMathLib for uint256; using Address for address; using EnumerableMap for EnumerableMap.UintToAddressMap; WETH public constant weth = WETH(payable(C.WETH)); // leveraged (w)eth vault ERC4626 public immutable scWETH; /** * @notice Enum indicating the purpose of a flashloan. */ enum FlashLoanType { Reallocate, ExitAllPositions } event EmergencyExitExecuted( address indexed admin, uint256 wethWithdrawn, uint256 debtRepaid, uint256 collateralReleased ); event Reallocated(); event Rebalanced(uint256 totalCollateral, uint256 totalDebt, uint256 floatBalance); event ProfitSold(uint256 wethSold, uint256 usdcReceived); event Supplied(uint256 adapterId, uint256 amount); event Borrowed(uint256 adapterId, uint256 amount); event Repaid(uint256 adapterId, uint256 amount); event Withdrawn(uint256 adapterId, uint256 amount); event Invested(uint256 wethAmount); event Disinvested(uint256 wethAmount); constructor(address _admin, address _keeper, ERC4626 _scWETH, PriceConverter _priceConverter, Swapper _swapper) BaseV2Vault(_admin, _keeper, ERC20(C.USDC), _priceConverter, _swapper, "Sandclock Yield USDC", "scUSDC") { _zeroAddressCheck(address(_scWETH)); scWETH = _scWETH; weth.safeApprove(address(scWETH), type(uint256).max); } /*////////////////////////////////////////////////////////////// PUBLIC API //////////////////////////////////////////////////////////////*/ /** * @notice Rebalance the vault's positions/loans in multiple lending markets. * @dev Called to increase or decrease the WETH debt to maintain the LTV (loan to value) and avoid liquidation. * @param _callData The encoded data for the calls to be made to the lending markets. */ function rebalance(bytes[] calldata _callData) external { _onlyKeeper(); _multiCall(_callData); // invest any weth remaining after rebalancing _invest(); // enforce float to be above the minimum required uint256 float = usdcBalance(); uint256 floatRequired = totalAssets().mulWadDown(floatPercentage); if (float < floatRequired) { revert FloatBalanceTooLow(float, floatRequired); } emit Rebalanced(totalCollateral(), totalDebt(), float); } /** * @notice Reallocate collateral & debt between lending markets, ie move debt and collateral positions from one lending market to another. * @dev To move the funds between lending markets, the vault uses flashloans to repay debt and release collateral in one lending market enabling it to be moved to anoter mm. * @param _flashLoanAmount The amount of WETH to flashloan from Balancer. Has to be at least equal to amount of WETH debt moved between lending markets. * @param _callData The encoded data for the calls to be made to the lending markets. */ function reallocate(uint256 _flashLoanAmount, bytes[] calldata _callData) external { _onlyKeeper(); if (_flashLoanAmount == 0) revert FlashLoanAmountZero(); address[] memory tokens = new address[](1); tokens[0] = address(weth); uint256[] memory amounts = new uint256[](1); amounts[0] = _flashLoanAmount; _initiateFlashLoan(); balancerVault.flashLoan(address(this), tokens, amounts, abi.encode(FlashLoanType.Reallocate, _callData)); _finalizeFlashLoan(); emit Reallocated(); } /** * @notice Sells WETH profits (swaps to USDC). * @dev As the vault generates yield by staking WETH, the profits are in WETH. * @param _usdcAmountOutMin The minimum amount of USDC to receive. */ function sellProfit(uint256 _usdcAmountOutMin) external { _onlyKeeper(); uint256 profit = _calculateWethProfit(wethInvested(), totalDebt()); if (profit == 0) revert NoProfitsToSell(); uint256 withdrawn = _disinvest(profit); uint256 usdcReceived = _swapWethForUsdc(withdrawn, _usdcAmountOutMin); emit ProfitSold(withdrawn, usdcReceived); } /** * @notice Emergency exit to disinvest everything, repay all debt and withdraw all collateral to the vault. * @dev In unlikely situation that the vault makes a loss on ETH staked, the total debt would be higher than ETH available to "unstake", * which can lead to withdrawals being blocked. To handle this situation, the vault can close all positions in all lending markets and release all of the assets (realize all losses). * @param _endUsdcBalanceMin The minimum USDC balance of the vault at the end of execution (after all positions are closed). */ function exitAllPositions(uint256 _endUsdcBalanceMin) external { _onlyKeeper(); uint256 collateral = totalCollateral(); uint256 debt = totalDebt(); uint256 wethBalance = scWETH.redeem(scWETH.balanceOf(address(this)), address(this), address(this)); if (debt > wethBalance) { // not enough WETH to repay all debt, flashloan the difference address[] memory tokens = new address[](1); tokens[0] = address(weth); uint256[] memory amounts = new uint256[](1); amounts[0] = debt - wethBalance; _initiateFlashLoan(); balancerVault.flashLoan(address(this), tokens, amounts, abi.encode(FlashLoanType.ExitAllPositions)); _finalizeFlashLoan(); } else { _repayAllDebtAndWithdrawCollateral(); // if some WETH remains after repaying all debt, swap it to USDC uint256 wethLeft = _wethBalance(); if (wethLeft != 0) _swapWethForUsdc(wethLeft, 0); } if (usdcBalance() < _endUsdcBalanceMin) revert EndUsdcBalanceTooLow(); emit EmergencyExitExecuted(msg.sender, wethBalance, debt, collateral); } /** * @notice Handles flashloan callbacks. * @dev Called by Balancer's vault in 2 situations: * 1. When the vault is underwater and the vault needs to exit all positions. * 2. When the vault needs to reallocate capital between lending markets. * @param _amounts single elment array containing the amount of WETH being flashloaned. * @param _data The encoded data that was passed to the flashloan. */ function receiveFlashLoan( address[] calldata, uint256[] calldata _amounts, uint256[] calldata _feeAmounts, bytes calldata _data ) external { _isFlashLoanInitiated(); uint256 flashLoanAmount = _amounts[0]; FlashLoanType flashLoanType = abi.decode(_data, (FlashLoanType)); if (flashLoanType == FlashLoanType.ExitAllPositions) { _repayAllDebtAndWithdrawCollateral(); _swapUsdcForExactWeth(flashLoanAmount); } else { (, bytes[] memory callData) = abi.decode(_data, (FlashLoanType, bytes[])); _multiCall(callData); } weth.safeTransfer(address(balancerVault), flashLoanAmount + _feeAmounts[0]); } /** * @notice Supply USDC assets to a lending market. * @param _adapterId The ID of the lending market adapter. * @param _amount The amount of USDC to supply. */ function supply(uint256 _adapterId, uint256 _amount) external { _onlyKeeperOrFlashLoan(); _isSupportedCheck(_adapterId); _supply(_adapterId, _amount); } /** * @notice Borrow WETH from a lending market. * @param _adapterId The ID of the lending market adapter. * @param _amount The amount of WETH to borrow. */ function borrow(uint256 _adapterId, uint256 _amount) external { _onlyKeeperOrFlashLoan(); _isSupportedCheck(_adapterId); _borrow(_adapterId, _amount); } /** * @notice Repay WETH to a lending market. * @param _adapterId The ID of the lending market adapter. * @param _amount The amount of WETH to repay. */ function repay(uint256 _adapterId, uint256 _amount) external { _onlyKeeperOrFlashLoan(); _isSupportedCheck(_adapterId); _repay(_adapterId, _amount); } /** * @notice Withdraw USDC assets from a lending market. * @param _adapterId The ID of the lending market adapter. * @param _amount The amount of USDC to withdraw. */ function withdraw(uint256 _adapterId, uint256 _amount) external { _onlyKeeperOrFlashLoan(); _isSupportedCheck(_adapterId); _withdraw(_adapterId, _amount); } /** * @notice Withdraw WETH from the staking vault (scWETH). * @param _amount The amount of WETH to withdraw. */ function disinvest(uint256 _amount) external { _onlyKeeper(); _disinvest(_amount); } /** * @notice total claimable assets of the vault in USDC. */ function totalAssets() public view override returns (uint256) { return _calculateTotalAssets(usdcBalance(), totalCollateral(), wethInvested(), totalDebt()); } /** * @notice Returns the USDC balance of the vault. */ function usdcBalance() public view returns (uint256) { return asset.balanceOf(address(this)); } /** * @notice Returns the USDC supplied as collateral in a lending market. * @param _adapterId The ID of the lending market adapter. */ function getCollateral(uint256 _adapterId) external view returns (uint256) { if (!isSupported(_adapterId)) return 0; return IAdapter(protocolAdapters.get(_adapterId)).getCollateral(address(this)); } /** * @notice Returns the total USDC supplied as collateral in all lending markets. */ function totalCollateral() public view returns (uint256 total) { uint256 length = protocolAdapters.length(); for (uint256 i = 0; i < length; i++) { (, address adapter) = protocolAdapters.at(i); total += IAdapter(adapter).getCollateral(address(this)); } } /** * @notice Returns the WETH borrowed from a lending market. * @param _adapterId The ID of the lending market adapter. */ function getDebt(uint256 _adapterId) external view returns (uint256) { if (!isSupported(_adapterId)) return 0; return IAdapter(protocolAdapters.get(_adapterId)).getDebt(address(this)); } /** * @notice Returns the total WETH borrowed in all lending markets. */ function totalDebt() public view returns (uint256 total) { uint256 length = protocolAdapters.length(); for (uint256 i = 0; i < length; i++) { (, address adapter) = protocolAdapters.at(i); total += IAdapter(adapter).getDebt(address(this)); } } /** * @notice Returns the amount of WETH invested (staked) in the leveraged WETH vault. */ function wethInvested() public view returns (uint256) { return scWETH.convertToAssets(scWETH.balanceOf(address(this))); } /** * @notice Returns the amount of profit (in WETH) made by the vault. * @dev The profit is calculated as the difference between the current WETH staked and the WETH owed. */ function getProfit() public view returns (uint256) { return _calculateWethProfit(wethInvested(), totalDebt()); } /*////////////////////////////////////////////////////////////// INTERNAL API //////////////////////////////////////////////////////////////*/ function _supply(uint256 _adapterId, uint256 _amount) internal { _adapterDelegateCall(_adapterId, abi.encodeWithSelector(IAdapter.supply.selector, _amount)); emit Supplied(_adapterId, _amount); } function _borrow(uint256 _adapterId, uint256 _amount) internal { _adapterDelegateCall(_adapterId, abi.encodeWithSelector(IAdapter.borrow.selector, _amount)); emit Borrowed(_adapterId, _amount); } function _repay(uint256 _adapterId, uint256 _amount) internal { uint256 wethBalance = _wethBalance(); _amount = _amount > wethBalance ? wethBalance : _amount; _adapterDelegateCall(_adapterId, abi.encodeWithSelector(IAdapter.repay.selector, _amount)); emit Repaid(_adapterId, _amount); } function _withdraw(uint256 _adapterId, uint256 _amount) internal { _adapterDelegateCall(_adapterId, abi.encodeWithSelector(IAdapter.withdraw.selector, _amount)); emit Withdrawn(_adapterId, _amount); } function _invest() internal { uint256 wethBalance = _wethBalance(); if (wethBalance > 0) { scWETH.deposit(wethBalance, address(this)); emit Invested(wethBalance); } } function _disinvest(uint256 _wethAmount) internal returns (uint256) { uint256 shares = scWETH.convertToShares(_wethAmount); uint256 amount = scWETH.redeem(shares, address(this), address(this)); emit Disinvested(amount); return amount; } function _repayAllDebtAndWithdrawCollateral() internal { uint256 length = protocolAdapters.length(); for (uint256 i = 0; i < length; i++) { (uint256 id, address adapter) = protocolAdapters.at(i); uint256 debt = IAdapter(adapter).getDebt(address(this)); uint256 collateral = IAdapter(adapter).getCollateral(address(this)); if (debt > 0) _repay(id, debt); if (collateral > 0) _withdraw(id, collateral); } } function beforeWithdraw(uint256 _assets, uint256) internal override { // here we need to make sure that the vault has enough assets to cover the withdrawal // the idea is to keep the same ltv after the withdrawal as before on every protocol uint256 initialBalance = usdcBalance(); if (initialBalance >= _assets) return; uint256 collateral = totalCollateral(); uint256 debt = totalDebt(); uint256 invested = wethInvested(); uint256 total = _calculateTotalAssets(initialBalance, collateral, invested, debt); uint256 profit = _calculateWethProfit(invested, debt); uint256 floatRequired = total > _assets ? (total - _assets).mulWadUp(floatPercentage) : 0; uint256 usdcNeeded = _assets + floatRequired - initialBalance; // first try to sell profits to cover withdrawal amount if (profit != 0) { uint256 withdrawn = _disinvest(profit); uint256 usdcAmountOutMin = priceConverter.ethToUsdc(withdrawn).mulWadDown(slippageTolerance); uint256 usdcReceived = _swapWethForUsdc(withdrawn, usdcAmountOutMin); if (initialBalance + usdcReceived >= _assets) return; usdcNeeded -= usdcReceived; } // if we still need more usdc, we need to repay debt and withdraw collateral _repayDebtAndReleaseCollateral(debt, collateral, invested, usdcNeeded); } function _repayDebtAndReleaseCollateral( uint256 _totalDebt, uint256 _totalCollateral, uint256 _invested, uint256 _usdcNeeded ) internal { // handle rounding errors when withdrawing everything _usdcNeeded = _usdcNeeded > _totalCollateral ? _totalCollateral : _usdcNeeded; // to keep the same ltv, total debt in weth to be repaid has to be proportional to total usdc collateral we are withdrawing uint256 wethNeeded = _usdcNeeded.mulDivUp(_totalDebt, _totalCollateral); wethNeeded = wethNeeded > _invested ? _invested : wethNeeded; uint256 wethDisinvested = 0; if (wethNeeded != 0) wethDisinvested = _disinvest(wethNeeded); // repay debt and withdraw collateral from each protocol in proportion to usdc supplied uint256 length = protocolAdapters.length(); for (uint256 i = 0; i < length; i++) { (uint256 id, address adapter) = protocolAdapters.at(i); uint256 collateral = IAdapter(adapter).getCollateral(address(this)); if (collateral == 0) continue; uint256 debt = IAdapter(adapter).getDebt(address(this)); uint256 toWithdraw = _usdcNeeded.mulDivUp(collateral, _totalCollateral); if (wethDisinvested != 0 && debt != 0) { // keep the same ltv when withdrawing usdc supplied from each protocol uint256 toRepay = toWithdraw.mulDivUp(debt, collateral); if (toRepay > wethDisinvested) { toRepay = wethDisinvested; } else { wethDisinvested -= toRepay; } _repay(id, toRepay); } _withdraw(id, toWithdraw); } } function _calculateTotalAssets(uint256 _float, uint256 _collateral, uint256 _invested, uint256 _debt) internal view returns (uint256 total) { total = _float + _collateral; uint256 profit = _calculateWethProfit(_invested, _debt); if (profit != 0) { // account for slippage when selling weth profits total += priceConverter.ethToUsdc(profit).mulWadDown(slippageTolerance); } else { total -= priceConverter.ethToUsdc(_debt - _invested); } } function _calculateWethProfit(uint256 _invested, uint256 _debt) internal pure returns (uint256) { return _invested > _debt ? _invested - _debt : 0; } function _wethBalance() internal view returns (uint256) { return weth.balanceOf(address(this)); } function _swapWethForUsdc(uint256 _wethAmount, uint256 _usdcAmountOutMin) internal returns (uint256) { bytes memory result = address(swapper).functionDelegateCall( abi.encodeWithSelector( Swapper.uniswapSwapExactInput.selector, weth, asset, _wethAmount, _usdcAmountOutMin, 500 /* pool fee*/ ) ); return abi.decode(result, (uint256)); } function _swapUsdcForExactWeth(uint256 _wethAmountOut) internal { address(swapper).functionDelegateCall( abi.encodeWithSelector( Swapper.uniswapSwapExactOutput.selector, asset, weth, _wethAmountOut, type(uint256).max, // ignore slippage 500 // pool fee ) ); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.10; error InvalidTargetLtv(); error InvalidMaxLtv(); error InvalidFlashLoanCaller(); error InvalidSlippageTolerance(); error InvalidFloatPercentage(); error ZeroAddress(); error PleaseUseRedeemMethod(); error FeesTooHigh(); error TreasuryCannotBeZero(); error VaultNotUnderwater(); error CallerNotAdmin(); error CallerNotKeeper(); error NoProfitsToSell(); error EndUsdcBalanceTooLow(); error InsufficientDepositBalance(); error AmountReceivedBelowMin(); error FlashLoanAmountZero(); error ProtocolNotSupported(uint256 adapterId); error ProtocolInUse(uint256 adapterId); error FloatBalanceTooLow(uint256 actual, uint256 required); error TokenOutNotAllowed(address token);
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "./ERC20.sol"; import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; /// @notice Minimalist and modern Wrapped Ether implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/WETH.sol) /// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol) contract WETH is ERC20("Wrapped Ether", "WETH", 18) { using SafeTransferLib for address; event Deposit(address indexed from, uint256 amount); event Withdrawal(address indexed to, uint256 amount); function deposit() public payable virtual { _mint(msg.sender, msg.value); emit Deposit(msg.sender, msg.value); } function withdraw(uint256 amount) public virtual { _burn(msg.sender, amount); emit Withdrawal(msg.sender, amount); msg.sender.safeTransferETH(amount); } receive() external payable virtual { deposit(); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol"; /// @notice Minimal ERC4626 tokenized Vault implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol) abstract contract ERC4626 is ERC20 { using SafeTransferLib for ERC20; using FixedPointMathLib for uint256; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /*////////////////////////////////////////////////////////////// IMMUTABLES //////////////////////////////////////////////////////////////*/ ERC20 public immutable asset; constructor( ERC20 _asset, string memory _name, string memory _symbol ) ERC20(_name, _symbol, _asset.decimals()) { asset = _asset; } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LOGIC //////////////////////////////////////////////////////////////*/ function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) { // Check for rounding error since we round down in previewDeposit. require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES"); // Need to transfer before minting or ERC777s could reenter. asset.safeTransferFrom(msg.sender, address(this), assets); _mint(receiver, shares); emit Deposit(msg.sender, receiver, assets, shares); afterDeposit(assets, shares); } function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) { assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up. // Need to transfer before minting or ERC777s could reenter. asset.safeTransferFrom(msg.sender, address(this), assets); _mint(receiver, shares); emit Deposit(msg.sender, receiver, assets, shares); afterDeposit(assets, shares); } function withdraw( uint256 assets, address receiver, address owner ) public virtual returns (uint256 shares) { shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up. if (msg.sender != owner) { uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; } beforeWithdraw(assets, shares); _burn(owner, shares); emit Withdraw(msg.sender, receiver, owner, assets, shares); asset.safeTransfer(receiver, assets); } function redeem( uint256 shares, address receiver, address owner ) public virtual returns (uint256 assets) { if (msg.sender != owner) { uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; } // Check for rounding error since we round down in previewRedeem. require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS"); beforeWithdraw(assets, shares); _burn(owner, shares); emit Withdraw(msg.sender, receiver, owner, assets, shares); asset.safeTransfer(receiver, assets); } /*////////////////////////////////////////////////////////////// ACCOUNTING LOGIC //////////////////////////////////////////////////////////////*/ function totalAssets() public view virtual returns (uint256); function convertToShares(uint256 assets) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets()); } function convertToAssets(uint256 shares) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply); } function previewDeposit(uint256 assets) public view virtual returns (uint256) { return convertToShares(assets); } function previewMint(uint256 shares) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply); } function previewWithdraw(uint256 assets) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets()); } function previewRedeem(uint256 shares) public view virtual returns (uint256) { return convertToAssets(shares); } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LIMIT LOGIC //////////////////////////////////////////////////////////////*/ function maxDeposit(address) public view virtual returns (uint256) { return type(uint256).max; } function maxMint(address) public view virtual returns (uint256) { return type(uint256).max; } function maxWithdraw(address owner) public view virtual returns (uint256) { return convertToAssets(balanceOf[owner]); } function maxRedeem(address owner) public view virtual returns (uint256) { return balanceOf[owner]; } /*////////////////////////////////////////////////////////////// INTERNAL HOOKS LOGIC //////////////////////////////////////////////////////////////*/ function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {} function afterDeposit(uint256 assets, uint256 shares) internal virtual {} }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { /*////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly assembly { // Transfer the ETH and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } require(success, "ETH_TRANSFER_FAILED"); } /*////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument. mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) ) } require(success, "TRANSFER_FROM_FAILED"); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "TRANSFER_FAILED"); } function safeApprove( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "APPROVE_FAILED"); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) library FixedPointMathLib { /*////////////////////////////////////////////////////////////// SIMPLIFIED FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ uint256 internal constant MAX_UINT256 = 2**256 - 1; uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. } function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. } function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. } function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. } /*////////////////////////////////////////////////////////////// LOW LEVEL FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ function mulDivDown( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // Divide x * y by the denominator. z := div(mul(x, y), denominator) } } function mulDivUp( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // If x * y modulo the denominator is strictly greater than 0, // 1 is added to round up the division of x * y by the denominator. z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator)) } } function rpow( uint256 x, uint256 n, uint256 scalar ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { switch x case 0 { switch n case 0 { // 0 ** 0 = 1 z := scalar } default { // 0 ** n = 0 z := 0 } } default { switch mod(n, 2) case 0 { // If n is even, store scalar in z for now. z := scalar } default { // If n is odd, store x in z for now. z := x } // Shifting right by 1 is like dividing by 2. let half := shr(1, scalar) for { // Shift n right by 1 before looping to halve it. n := shr(1, n) } n { // Shift n right by 1 each iteration to halve it. n := shr(1, n) } { // Revert immediately if x ** 2 would overflow. // Equivalent to iszero(eq(div(xx, x), x)) here. if shr(128, x) { revert(0, 0) } // Store x squared. let xx := mul(x, x) // Round to the nearest number. let xxRound := add(xx, half) // Revert if xx + half overflowed. if lt(xxRound, xx) { revert(0, 0) } // Set x to scaled xxRound. x := div(xxRound, scalar) // If n is even: if mod(n, 2) { // Compute z * x. let zx := mul(z, x) // If z * x overflowed: if iszero(eq(div(zx, x), z)) { // Revert if x is non-zero. if iszero(iszero(x)) { revert(0, 0) } } // Round to the nearest number. let zxRound := add(zx, half) // Revert if zx + half overflowed. if lt(zxRound, zx) { revert(0, 0) } // Return properly scaled zxRound. z := div(zxRound, scalar) } } } } } /*////////////////////////////////////////////////////////////// GENERAL NUMBER UTILITIES //////////////////////////////////////////////////////////////*/ function sqrt(uint256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { let y := x // We start y at x, which will help us make our initial estimate. z := 181 // The "correct" value is 1, but this saves a multiplication later. // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. // We check y >= 2^(k + 8) but shift right by k bits // each branch to ensure that if x >= 256, then y >= 256. if iszero(lt(y, 0x10000000000000000000000000000000000)) { y := shr(128, y) z := shl(64, z) } if iszero(lt(y, 0x1000000000000000000)) { y := shr(64, y) z := shl(32, z) } if iszero(lt(y, 0x10000000000)) { y := shr(32, y) z := shl(16, z) } if iszero(lt(y, 0x1000000)) { y := shr(16, y) z := shl(8, z) } // Goal was to get z*z*y within a small factor of x. More iterations could // get y in a tighter range. Currently, we will have y in [256, 256*2^16). // We ensured y >= 256 so that the relative difference between y and y+1 is small. // That's not possible if x < 256 but we can just verify those cases exhaustively. // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256. // Correctness can be checked exhaustively for x < 256, so we assume y >= 256. // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps. // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256. // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18. // There is no overflow risk here since y < 2^136 after the first branch above. z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181. // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // If x+1 is a perfect square, the Babylonian method cycles between // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor. // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case. // If you don't care whether the floor or ceil square root is returned, you can remove this statement. z := sub(z, lt(div(x, z), z)) } } function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Mod x by y. Note this will return // 0 instead of reverting if y is zero. z := mod(x, y) } } function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { // Divide x by y. Note this will return // 0 instead of reverting if y is zero. r := div(x, y) } } function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Add 1 to x * y if x % y > 0. Note this will // return 0 instead of reverting if y is zero. z := add(gt(mod(x, y), 0), div(x, y)) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @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://diligence.consensys.net/posts/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 Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(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); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableMap.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableMap.js. pragma solidity ^0.8.0; import "./EnumerableSet.sol"; /** * @dev Library for managing an enumerable variant of Solidity's * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] * type. * * Maps have the following properties: * * - Entries are added, removed, and checked for existence in constant time * (O(1)). * - Entries are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableMap for EnumerableMap.UintToAddressMap; * * // Declare a set state variable * EnumerableMap.UintToAddressMap private myMap; * } * ``` * * The following map types are supported: * * - `uint256 -> address` (`UintToAddressMap`) since v3.0.0 * - `address -> uint256` (`AddressToUintMap`) since v4.6.0 * - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0 * - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0 * - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0 * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableMap. * ==== */ library EnumerableMap { using EnumerableSet for EnumerableSet.Bytes32Set; // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Map type with // bytes32 keys and values. // The Map implementation uses private functions, and user-facing // implementations (such as Uint256ToAddressMap) are just wrappers around // the underlying Map. // This means that we can only create new EnumerableMaps for types that fit // in bytes32. struct Bytes32ToBytes32Map { // Storage of keys EnumerableSet.Bytes32Set _keys; mapping(bytes32 => bytes32) _values; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set( Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value ) internal returns (bool) { map._values[key] = value; return map._keys.add(key); } /** * @dev Removes a key-value pair from a map. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) { delete map._values[key]; return map._keys.remove(key); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) { return map._keys.contains(key); } /** * @dev Returns the number of key-value pairs in the map. O(1). */ function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) { return map._keys.length(); } /** * @dev Returns the key-value pair stored at position `index` in the map. O(1). * * Note that there are no guarantees on the ordering of entries inside the * array, and it may change when more entries are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32, bytes32) { bytes32 key = map._keys.at(index); return (key, map._values[key]); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. */ function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) { bytes32 value = map._values[key]; if (value == bytes32(0)) { return (contains(map, key), bytes32(0)); } else { return (true, value); } } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) { bytes32 value = map._values[key]; require(value != 0 || contains(map, key), "EnumerableMap: nonexistent key"); return value; } /** * @dev Same as {get}, with a custom error message when `key` is not in the map. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryGet}. */ function get( Bytes32ToBytes32Map storage map, bytes32 key, string memory errorMessage ) internal view returns (bytes32) { bytes32 value = map._values[key]; require(value != 0 || contains(map, key), errorMessage); return value; } // UintToUintMap struct UintToUintMap { Bytes32ToBytes32Map _inner; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set( UintToUintMap storage map, uint256 key, uint256 value ) internal returns (bool) { return set(map._inner, bytes32(key), bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(UintToUintMap storage map, uint256 key) internal returns (bool) { return remove(map._inner, bytes32(key)); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) { return contains(map._inner, bytes32(key)); } /** * @dev Returns the number of elements in the map. O(1). */ function length(UintToUintMap storage map) internal view returns (uint256) { return length(map._inner); } /** * @dev Returns the element stored at position `index` in the set. O(1). * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintToUintMap storage map, uint256 index) internal view returns (uint256, uint256) { (bytes32 key, bytes32 value) = at(map._inner, index); return (uint256(key), uint256(value)); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. */ function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) { (bool success, bytes32 value) = tryGet(map._inner, bytes32(key)); return (success, uint256(value)); } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) { return uint256(get(map._inner, bytes32(key))); } /** * @dev Same as {get}, with a custom error message when `key` is not in the map. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryGet}. */ function get( UintToUintMap storage map, uint256 key, string memory errorMessage ) internal view returns (uint256) { return uint256(get(map._inner, bytes32(key), errorMessage)); } // UintToAddressMap struct UintToAddressMap { Bytes32ToBytes32Map _inner; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set( UintToAddressMap storage map, uint256 key, address value ) internal returns (bool) { return set(map._inner, bytes32(key), bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) { return remove(map._inner, bytes32(key)); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) { return contains(map._inner, bytes32(key)); } /** * @dev Returns the number of elements in the map. O(1). */ function length(UintToAddressMap storage map) internal view returns (uint256) { return length(map._inner); } /** * @dev Returns the element stored at position `index` in the set. O(1). * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) { (bytes32 key, bytes32 value) = at(map._inner, index); return (uint256(key), address(uint160(uint256(value)))); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. */ function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) { (bool success, bytes32 value) = tryGet(map._inner, bytes32(key)); return (success, address(uint160(uint256(value)))); } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(UintToAddressMap storage map, uint256 key) internal view returns (address) { return address(uint160(uint256(get(map._inner, bytes32(key))))); } /** * @dev Same as {get}, with a custom error message when `key` is not in the map. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryGet}. */ function get( UintToAddressMap storage map, uint256 key, string memory errorMessage ) internal view returns (address) { return address(uint160(uint256(get(map._inner, bytes32(key), errorMessage)))); } // AddressToUintMap struct AddressToUintMap { Bytes32ToBytes32Map _inner; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set( AddressToUintMap storage map, address key, uint256 value ) internal returns (bool) { return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(AddressToUintMap storage map, address key) internal returns (bool) { return remove(map._inner, bytes32(uint256(uint160(key)))); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(AddressToUintMap storage map, address key) internal view returns (bool) { return contains(map._inner, bytes32(uint256(uint160(key)))); } /** * @dev Returns the number of elements in the map. O(1). */ function length(AddressToUintMap storage map) internal view returns (uint256) { return length(map._inner); } /** * @dev Returns the element stored at position `index` in the set. O(1). * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) { (bytes32 key, bytes32 value) = at(map._inner, index); return (address(uint160(uint256(key))), uint256(value)); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. */ function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) { (bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key)))); return (success, uint256(value)); } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(AddressToUintMap storage map, address key) internal view returns (uint256) { return uint256(get(map._inner, bytes32(uint256(uint160(key))))); } /** * @dev Same as {get}, with a custom error message when `key` is not in the map. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryGet}. */ function get( AddressToUintMap storage map, address key, string memory errorMessage ) internal view returns (uint256) { return uint256(get(map._inner, bytes32(uint256(uint160(key))), errorMessage)); } // Bytes32ToUintMap struct Bytes32ToUintMap { Bytes32ToBytes32Map _inner; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set( Bytes32ToUintMap storage map, bytes32 key, uint256 value ) internal returns (bool) { return set(map._inner, key, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) { return remove(map._inner, key); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) { return contains(map._inner, key); } /** * @dev Returns the number of elements in the map. O(1). */ function length(Bytes32ToUintMap storage map) internal view returns (uint256) { return length(map._inner); } /** * @dev Returns the element stored at position `index` in the set. O(1). * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32, uint256) { (bytes32 key, bytes32 value) = at(map._inner, index); return (key, uint256(value)); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. */ function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool, uint256) { (bool success, bytes32 value) = tryGet(map._inner, key); return (success, uint256(value)); } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) { return uint256(get(map._inner, key)); } /** * @dev Same as {get}, with a custom error message when `key` is not in the map. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryGet}. */ function get( Bytes32ToUintMap storage map, bytes32 key, string memory errorMessage ) internal view returns (uint256) { return uint256(get(map._inner, key, errorMessage)); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.13; library Constants { // address of the multisig. meant to have default admin role address public constant MULTISIG = 0x035F210e5d14054E8AE5A6CFA76d643aA200D56E; uint256 public constant ONE = 1e18; // decimals difference between WETH and USDC (18 - 6) uint256 public constant WETH_USDC_DECIMALS_DIFF = 1e12; // value for the variable interest rate mode on Aave uint256 public constant AAVE_VAR_INTEREST_RATE_MODE = 2; // enable efficeincy mode on Aave (used to allow greater LTV when asset and debt tokens are correlated in price) uint8 public constant AAVE_EMODE_ID = 1; // vaule used to scale the token's collateral/borrow factors from the euler market uint32 constant EULER_CONFIG_FACTOR_SCALE = 4_000_000_000; /*////////////////////////////////////////////////////////////// MAINNET ADDRESSES //////////////////////////////////////////////////////////////*/ // address of the USDC token contract address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; // address of the WETH token contract address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; // address of the wrapped stETH token contract address public constant WSTETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; // address of the Lido stETH token contract address public constant STETH = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84; // address of the LUSD token contract address public constant LUSD = 0x5f98805A4E8be255a32880FDeC7F6728C6568bA0; // address of the Curve pool for ETH-stETH address public constant CURVE_ETH_STETH_POOL = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022; // address of the Uniswap v3 swap router contract address public constant UNISWAP_V3_SWAP_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564; // address of the Aave v3 pool contract address public constant AAVE_V3_POOL = 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2; // address of the Aave pool data provider contract address public constant AAVE_V3_POOL_DATA_PROVIDER = 0x7B4EB56E7CD4b454BA8ff71E4518426369a138a3; // address of the Aave v3 "aEthUSDC" token (supply token) address public constant AAVE_V3_AUSDC_TOKEN = 0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c; // address of the Aave v3 "aEthwstETH" token (supply token) address public constant AAVE_V3_AWSTETH_TOKEN = 0x0B925eD163218f6662a35e0f0371Ac234f9E9371; // address of the Aave v3 "variableDebtEthWETH" token (variable debt token) address public constant AAVE_V3_VAR_DEBT_WETH_TOKEN = 0xeA51d7853EEFb32b6ee06b1C12E6dcCA88Be0fFE; // address of the Aave v3 "variableDebtEthWETH" token implementation contract (variable debt token) address public constant AAVE_V3_VAR_DEBT_IMPLEMENTATION_CONTRACT = 0xaC725CB59D16C81061BDeA61041a8A5e73DA9EC6; // EULER Contracts address public constant EULER = 0x27182842E098f60e3D576794A5bFFb0777E025d3; address public constant EULER_MARKETS = 0x3520d5a913427E6F0D6A83E07ccD4A4da316e4d3; // Euler supply token for wstETH (ewstETH) address public constant EULER_ETOKEN_WSTETH = 0xbd1bd5C956684f7EB79DA40f582cbE1373A1D593; // Euler supply token for USDC (eUSDC) address public constant EULER_ETOKEN_USDC = 0xEb91861f8A4e1C12333F42DCE8fB0Ecdc28dA716; // Euler debt token weth address public constant EULER_DTOKEN_WETH = 0x62e28f054efc24b26A794F5C1249B6349454352C; // address of the EULER rewards token contract address public constant EULER_REWARDS_TOKEN = 0xd9Fcd98c322942075A5C3860693e9f4f03AAE07b; // adress of the Chainlink aggregator for the USDC/eth price feed address public constant CHAINLINK_USDC_ETH_PRICE_FEED = 0x986b5E1e1755e3C2440e960477f25201B0a8bbD4; // Chainlink pricefeed (stETH -> ETH) address public constant CHAINLINK_STETH_ETH_PRICE_FEED = 0x86392dC19c0b719886221c78AB11eb8Cf5c52812; // Liquity pricefeed (USD -> ETH) with Chainlink as primary and Tellor as backup. address public constant LIQUITY_USD_ETH_PRICE_FEED = 0x4c517D4e2C851CA76d7eC94B805269Df0f2201De; // address of the Balancer vault contract address public constant BALANCER_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; // Balancer admin account address public constant BALANCER_ADMIN = 0x97207B095e4D5C9a6e4cfbfcd2C3358E03B90c4A; // address of the Balance Protocol Fees Collector contract address public constant BALANCER_FEES_COLLECTOR = 0xce88686553686DA562CE7Cea497CE749DA109f9F; // address of the 0x swap router contract address public constant ZERO_EX_ROUTER = 0xDef1C0ded9bec7F1a1670819833240f027b25EfF; // Compound v3 address public constant COMPOUND_V3_COMET_WETH = 0xA17581A9E3356d9A858b789D68B4d866e593aE94; // Aave v2 lending pool address public constant AAVE_V2_LENDING_POOL = 0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9; // Aave v2 protocol data provider address public constant AAVE_V2_PROTOCOL_DATA_PROVIDER = 0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d; // Aave v2 interest bearing USDC (aUSDC) token address public constant AAVE_V2_AUSDC_TOKEN = 0xBcca60bB61934080951369a648Fb03DF4F96263C; // Aave v2 variable debt bearing WETH (variableDebtWETH) token address public constant AAVE_V2_VAR_DEBT_WETH_TOKEN = 0xF63B34710400CAd3e044cFfDcAb00a0f32E33eCf; // Liquity address public constant LIQUITY_STABILITY_POOL = 0x66017D22b0f8556afDd19FC67041899Eb65a21bb; address public constant LIQUITY_LQTY_TOKEN = 0x6DEA81C8171D0bA574754EF6F8b412F2Ed88c54D; // Morpho address public constant MORPHO = 0x33333aea097c193e66081E930c33020272b33333; }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; import {TokenOutNotAllowed} from "../errors/scErrors.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; import {Address} from "openzeppelin-contracts/utils/Address.sol"; import {EnumerableMap} from "openzeppelin-contracts/utils/structs/EnumerableMap.sol"; import {ProtocolNotSupported, ProtocolInUse, ZeroAddress} from "../errors/scErrors.sol"; import {Constants as C} from "../lib/Constants.sol"; import {IVault} from "../interfaces/balancer/IVault.sol"; import {IFlashLoanRecipient} from "../interfaces/balancer/IFlashLoanRecipient.sol"; import {IAdapter} from "./IAdapter.sol"; import {PriceConverter} from "./PriceConverter.sol"; import {Swapper} from "./Swapper.sol"; import {sc4626} from "../sc4626.sol"; /** * @title BaseV2Vault * @notice Base vault contract for v2 vaults to that use multiple lending markets thru adapters. */ abstract contract BaseV2Vault is sc4626, IFlashLoanRecipient { using Address for address; using EnumerableMap for EnumerableMap.UintToAddressMap; event SwapperUpdated(address indexed admin, address newSwapper); event ProtocolAdapterAdded(address indexed admin, uint256 adapterId, address adapter); event ProtocolAdapterRemoved(address indexed admin, uint256 adapterId); event RewardsClaimed(uint256 adapterId); event TokenSwapped(address token, uint256 amount, uint256 amountReceived); event TokenWhitelisted(address token, bool value); // Balancer vault for flashloans IVault public constant balancerVault = IVault(C.BALANCER_VAULT); // price converter contract PriceConverter public immutable priceConverter; // swapper contract for facilitating token swaps Swapper public swapper; // mapping of IDs to lending protocol adapter contracts EnumerableMap.UintToAddressMap internal protocolAdapters; // mapping for the tokenOuts allowed during zeroExSwap mapping(ERC20 => bool) internal zeroExSwapWhitelist; constructor( address _admin, address _keeper, ERC20 _asset, PriceConverter _priceConverter, Swapper _swapper, string memory _name, string memory _symbol ) sc4626(_admin, _keeper, _asset, _name, _symbol) { _zeroAddressCheck(address(_priceConverter)); _zeroAddressCheck(address(_swapper)); priceConverter = _priceConverter; swapper = _swapper; zeroExSwapWhitelist[_asset] = true; } /** * @notice whitelist (or cancel whitelist) a token to be swapped out using zeroExSwap * @param _token The token to whitelist * @param _value whether to whitelist or cancel whitelist */ function whiteListOutToken(ERC20 _token, bool _value) external { _onlyAdmin(); if (address(_token) == address(0)) revert ZeroAddress(); zeroExSwapWhitelist[_token] = _value; emit TokenWhitelisted(address(_token), _value); } /** * @notice Set the swapper contract used for executing token swaps. * @param _newSwapper The new swapper contract. */ function setSwapper(Swapper _newSwapper) external { _onlyAdmin(); if (address(_newSwapper) == address(0)) revert ZeroAddress(); swapper = _newSwapper; emit SwapperUpdated(msg.sender, address(_newSwapper)); } /** * @notice Add a new protocol adapter to the vault. * @param _adapter The adapter to add. */ function addAdapter(IAdapter _adapter) external { _onlyAdmin(); uint256 id = _adapter.id(); if (isSupported(id)) revert ProtocolInUse(id); protocolAdapters.set(id, address(_adapter)); address(_adapter).functionDelegateCall(abi.encodeWithSelector(IAdapter.setApprovals.selector)); emit ProtocolAdapterAdded(msg.sender, id, address(_adapter)); } /** * @notice Remove a protocol adapter from the vault. Reverts if the adapter is in use unless _force is true. * @param _adapterId The ID of the adapter to remove. * @param _force Whether or not to force the removal of the adapter. */ function removeAdapter(uint256 _adapterId, bool _force) external { _onlyAdmin(); _isSupportedCheck(_adapterId); // check if protocol is being used if (!_force && IAdapter(protocolAdapters.get(_adapterId)).getCollateral(address(this)) > 0) { revert ProtocolInUse(_adapterId); } _adapterDelegateCall(_adapterId, abi.encodeWithSelector(IAdapter.revokeApprovals.selector)); protocolAdapters.remove(_adapterId); emit ProtocolAdapterRemoved(msg.sender, _adapterId); } /** * @notice Check if a lending market adapter is supported/used. * @param _adapterId The ID of the lending market adapter. */ function isSupported(uint256 _adapterId) public view returns (bool) { return protocolAdapters.contains(_adapterId); } /** * @notice returns whether a token is whitelisted to be swapped out using zeroExSwap or not */ function isTokenWhitelisted(ERC20 _token) external view returns (bool) { return zeroExSwapWhitelist[_token]; } /** * @notice Claim rewards from a lending market. * @param _adapterId The ID of the lending market adapter. * @param _callData The encoded data for the claimRewards function. */ function claimRewards(uint256 _adapterId, bytes calldata _callData) external { _onlyKeeperOrFlashLoan(); _isSupportedCheck(_adapterId); _adapterDelegateCall(_adapterId, abi.encodeWithSelector(IAdapter.claimRewards.selector, _callData)); emit RewardsClaimed(_adapterId); } /** * @notice Sell any token for the "asset" token on 0x exchange. * @param _tokenIn The token to sell. * @param _tokenOut The token to buy. * @param _amount The amount of tokens to sell. * @param _swapData The swap data for 0xrouter. * @param _assetAmountOutMin The minimum amount of "asset" token to receive for the swap. */ function zeroExSwap( ERC20 _tokenIn, ERC20 _tokenOut, uint256 _amount, bytes calldata _swapData, uint256 _assetAmountOutMin ) external { _onlyKeeperOrFlashLoan(); if (!zeroExSwapWhitelist[_tokenOut]) revert TokenOutNotAllowed(address(_tokenOut)); bytes memory result = address(swapper).functionDelegateCall( abi.encodeWithSelector( Swapper.zeroExSwap.selector, _tokenIn, _tokenOut, _amount, _assetAmountOutMin, _swapData ) ); emit TokenSwapped(address(_tokenIn), _amount, abi.decode(result, (uint256))); } function _multiCall(bytes[] memory _callData) internal { for (uint256 i = 0; i < _callData.length; i++) { if (_callData[i].length == 0) continue; address(this).functionDelegateCall(_callData[i]); } } function _adapterDelegateCall(uint256 _adapterId, bytes memory _data) internal { protocolAdapters.get(_adapterId).functionDelegateCall(_data); } function _adapterDelegateCall(address _adapter, bytes memory _data) internal { _adapter.functionDelegateCall(_data); } function _isSupportedCheck(uint256 _adapterId) internal view { if (!isSupported(_adapterId)) revert ProtocolNotSupported(_adapterId); } function _zeroAddressCheck(address _address) internal pure { if (_address == address(0)) revert ZeroAddress(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); function getRoundData(uint80 _roundId) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; /** * @notice Interface for adapters that allow interactions with the lending protocols */ interface IAdapter { /** * @notice Returns the adapter's ID */ function id() external view returns (uint256); /** * @notice Sets the necessary approvals (allowances) for interacting with the lending protocol */ function setApprovals() external; /** * @notice Removes the given approvals (allowances) for interacting with the lending protocol */ function revokeApprovals() external; /** * @notice Supplies the given amount of collateral to the lending protocol * @param amount The amount of collateral to supply */ function supply(uint256 amount) external; /** * @notice Borrows the given amount of debt from the lending protocol * @param amount The amount of debt to borrow */ function borrow(uint256 amount) external; /** * @notice Repays the given amount of debt to the lending protocol * @param amount The amount of debt to repay */ function repay(uint256 amount) external; /** * @notice Withdraws the given amount of collateral from the lending protocol * @param amount The amount of collateral to withdraw */ function withdraw(uint256 amount) external; /** * @notice Claims rewards awarded by the lending protocol * @param data Any data needed for the claim process */ function claimRewards(bytes calldata data) external; /** * @notice Returns the amount of collateral currently supplied to the lending protocol * @param account The account to check */ function getCollateral(address account) external view returns (uint256); /** * @notice Returns the amount of debt currently borrowed from the lending protocol * @param account The account to check */ function getDebt(address account) external view returns (uint256); /** * @notice Returns the maximum loan-to-value (LTV) ratio for the lending protocol */ function getMaxLtv() external view returns (uint256); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; import {AccessControl} from "openzeppelin-contracts/access/AccessControl.sol"; import {ZeroAddress, CallerNotAdmin} from "../errors/scErrors.sol"; import {Constants as C} from "../lib/Constants.sol"; import {AggregatorV3Interface} from "../interfaces/chainlink/AggregatorV3Interface.sol"; import {IwstETH} from "../interfaces/lido/IwstETH.sol"; /** * @title Price Converter * @notice Contract for price conversion between assets used by staking vaults. */ contract PriceConverter is AccessControl { using FixedPointMathLib for uint256; IwstETH constant wstETH = IwstETH(C.WSTETH); event UsdcToEthPriceFeedUpdated(address indexed admin, address newPriceFeed); event StEthToEthPriceFeedUpdated(address indexed admin, address newPriceFeed); // Chainlink price feed (USDC -> ETH) AggregatorV3Interface public usdcToEthPriceFeed = AggregatorV3Interface(C.CHAINLINK_USDC_ETH_PRICE_FEED); // Chainlink price feed (stETH -> ETH) AggregatorV3Interface public stEThToEthPriceFeed = AggregatorV3Interface(C.CHAINLINK_STETH_ETH_PRICE_FEED); constructor(address _admin) { _zeroAddressCheck(_admin); _grantRole(DEFAULT_ADMIN_ROLE, _admin); } function _onlyAdmin() internal view { if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) revert CallerNotAdmin(); } /** * @notice Set the chainlink price feed for USDC -> WETH. * @param _newPriceFeed The new price feed. */ function setUsdcToEthPriceFeed(address _newPriceFeed) external { _onlyAdmin(); _zeroAddressCheck(_newPriceFeed); usdcToEthPriceFeed = AggregatorV3Interface(_newPriceFeed); emit UsdcToEthPriceFeedUpdated(msg.sender, address(_newPriceFeed)); } /// @notice Set the chainlink price feed for stETH -> ETH. /// @param _newPriceFeed The new price feed. function setStEThToEthPriceFeed(address _newPriceFeed) external { _onlyAdmin(); _zeroAddressCheck(_newPriceFeed); stEThToEthPriceFeed = AggregatorV3Interface(_newPriceFeed); emit StEthToEthPriceFeedUpdated(msg.sender, address(_newPriceFeed)); } /** * @notice Returns the USDC fair value for the ETH amount provided. * @param _ethAmount The amount of ETH. */ function ethToUsdc(uint256 _ethAmount) public view returns (uint256) { (, int256 usdcPriceInEth,,,) = usdcToEthPriceFeed.latestRoundData(); return _ethAmount.divWadDown(uint256(usdcPriceInEth) * C.WETH_USDC_DECIMALS_DIFF); } /** * @notice Returns the ETH fair value for the USDC amount provided. * @param _usdcAmount The amount of USDC. */ function usdcToEth(uint256 _usdcAmount) public view returns (uint256) { (, int256 usdcPriceInEth,,,) = usdcToEthPriceFeed.latestRoundData(); return (_usdcAmount * C.WETH_USDC_DECIMALS_DIFF).mulWadDown(uint256(usdcPriceInEth)); } function ethToWstEth(uint256 ethAmount) public view returns (uint256) { (, int256 price,,,) = stEThToEthPriceFeed.latestRoundData(); uint256 stEthAmount = ethAmount.divWadDown(uint256(price)); return wstETH.getWstETHByStETH(stEthAmount); } function stEthToEth(uint256 _stEthAmount) public view returns (uint256) { (, int256 price,,,) = stEThToEthPriceFeed.latestRoundData(); return _stEthAmount.mulWadDown(uint256(price)); } function wstEthToEth(uint256 wstEthAmount) public view returns (uint256) { // wstETh to stEth using exchangeRate uint256 stEthAmount = wstETH.getStETHByWstETH(wstEthAmount); return stEthToEth(stEthAmount); } function _zeroAddressCheck(address _address) internal pure { if (_address == address(0)) revert ZeroAddress(); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.19; import {ERC20} from "solmate/tokens/ERC20.sol"; import {WETH} from "solmate/tokens/WETH.sol"; import {ILido} from "../interfaces/lido/ILido.sol"; import {IwstETH} from "../interfaces/lido/IwstETH.sol"; import {ICurvePool} from "../interfaces/curve/ICurvePool.sol"; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {Address} from "openzeppelin-contracts/utils/Address.sol"; import {AmountReceivedBelowMin} from "../errors/scErrors.sol"; import {ISwapRouter} from "../interfaces/uniswap/ISwapRouter.sol"; import {Constants as C} from "../lib/Constants.sol"; /** * @title Swapper * @notice Contract facilitating token swaps on Uniswap V3 and 0x. * @dev This contract is only meant to be used via delegatecalls from another contract. * @dev Using this contract directly for swaps might result in reverts. */ contract Swapper { using SafeTransferLib for ERC20; using Address for address; // Uniswap V3 router ISwapRouter public constant swapRouter = ISwapRouter(C.UNISWAP_V3_SWAP_ROUTER); ICurvePool public constant curvePool = ICurvePool(C.CURVE_ETH_STETH_POOL); WETH public constant weth = WETH(payable(C.WETH)); ILido public constant stEth = ILido(C.STETH); IwstETH public constant wstETH = IwstETH(C.WSTETH); /** * @notice Swap tokens on Uniswap V3 using exact input single function. * @param _tokenIn Address of the token to swap. * @param _tokenOut Address of the token to receive. * @param _amountIn Amount of the token to swap. * @param _amountOutMin Minimum amount of the token to receive. */ function uniswapSwapExactInput( ERC20 _tokenIn, ERC20 _tokenOut, uint256 _amountIn, uint256 _amountOutMin, uint24 _poolFee ) external returns (uint256) { ERC20(_tokenIn).safeApprove(address(swapRouter), _amountIn); ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({ tokenIn: address(_tokenIn), tokenOut: address(_tokenOut), fee: _poolFee, recipient: address(this), deadline: block.timestamp, amountIn: _amountIn, amountOutMinimum: _amountOutMin, sqrtPriceLimitX96: 0 }); return swapRouter.exactInputSingle(params); } /** * @notice Swap tokens on Uniswap V3 using exact output single function. * @param _tokenIn Address of the token to swap. * @param _tokenOut Address of the token to receive. * @param _amountOut Amount of the token to receive. * @param _amountInMaximum Maximum amount of the token to swap. */ function uniswapSwapExactOutput( ERC20 _tokenIn, ERC20 _tokenOut, uint256 _amountOut, uint256 _amountInMaximum, uint24 _poolFee ) external returns (uint256) { ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter.ExactOutputSingleParams({ tokenIn: address(_tokenIn), tokenOut: address(_tokenOut), fee: _poolFee, recipient: address(this), deadline: block.timestamp, amountOut: _amountOut, amountInMaximum: _amountInMaximum, sqrtPriceLimitX96: 0 }); _tokenIn.safeApprove(address(swapRouter), _amountInMaximum); uint256 amountIn = swapRouter.exactOutputSingle(params); _tokenIn.safeApprove(address(swapRouter), 0); return amountIn; } /** * @notice Swap tokens on 0x protocol. * @param _tokenIn Address of the token to swap. * @param _tokenOut Address of the token to receive. * @param _amountIn Amount of the token to swap. * @param _amountOutMin Minimum amount of the token to receive. * @param _swapData Encoded swap data obtained from 0x API. */ function zeroExSwap( ERC20 _tokenIn, ERC20 _tokenOut, uint256 _amountIn, uint256 _amountOutMin, bytes calldata _swapData ) external returns (uint256) { uint256 tokenOutInitialBalance = _tokenOut.balanceOf(address(this)); _tokenIn.safeApprove(C.ZERO_EX_ROUTER, _amountIn); C.ZERO_EX_ROUTER.functionCall(_swapData); uint256 amountReceived = _tokenOut.balanceOf(address(this)) - tokenOutInitialBalance; if (amountReceived < _amountOutMin) revert AmountReceivedBelowMin(); _tokenIn.approve(C.ZERO_EX_ROUTER, 0); return amountReceived; } function lidoSwapWethToWstEth(uint256 _wethAmount) external { // weth to eth weth.withdraw(_wethAmount); // stake to lido / eth => stETH stEth.submit{value: _wethAmount}(address(0x00)); // stETH to wstEth uint256 stEthBalance = stEth.balanceOf(address(this)); ERC20(address(stEth)).safeApprove(address(wstETH), stEthBalance); wstETH.wrap(stEthBalance); } function curveSwapStEthToWeth(uint256 _stEthAmount, uint256 _wethAmountOutMin) external returns (uint256 wethReceived) { // stETH to eth ERC20(address(stEth)).safeApprove(address(curvePool), _stEthAmount); wethReceived = curvePool.exchange(1, 0, _stEthAmount, _wethAmountOutMin); // eth to weth weth.deposit{value: address(this).balance}(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IVault { /** * @dev Performs a 'flash loan', sending tokens to `recipient`, executing the `receiveFlashLoan` hook on it, * and then reverting unless the tokens plus a proportional protocol fee have been returned. * * The `tokens` and `amounts` arrays must have the same length, and each entry in these indicates the loan amount * for each token contract. `tokens` must be sorted in ascending order. * * The 'userData' field is ignored by the Vault, and forwarded as-is to `recipient` as part of the * `receiveFlashLoan` call. * * Emits `FlashLoan` events. */ function flashLoan(address recipient, address[] memory tokens, uint256[] memory amounts, bytes memory userData) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // Inspired by Aave Protocol's IFlashLoanReceiver. interface IFlashLoanRecipient { /** * @dev When `flashLoan` is called on the Vault, it invokes the `receiveFlashLoan` hook on the recipient. * * At the time of the call, the Vault will have transferred `amounts` for `tokens` to the recipient. Before this * call returns, the recipient must have transferred `amounts` plus `feeAmounts` for each token back to the * Vault, or else the entire flash loan will revert. * * `userData` is the same value passed in the `IVault.flashLoan` call. */ function receiveFlashLoan( address[] memory tokens, uint256[] memory amounts, uint256[] memory feeAmounts, bytes memory userData ) external; }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.10; import {ERC20} from "solmate/tokens/ERC20.sol"; import {ERC4626} from "solmate/mixins/ERC4626.sol"; import {AccessControl} from "openzeppelin-contracts/access/AccessControl.sol"; import {Constants as C} from "./lib/Constants.sol"; import { CallerNotAdmin, CallerNotKeeper, ZeroAddress, InvalidFlashLoanCaller, TreasuryCannotBeZero, FeesTooHigh, InvalidFloatPercentage, InvalidSlippageTolerance } from "./errors/scErrors.sol"; abstract contract sc4626 is ERC4626, AccessControl { constructor(address _admin, address _keeper, ERC20 _asset, string memory _name, string memory _symbol) ERC4626(_asset, _name, _symbol) { if (_admin == address(0)) revert ZeroAddress(); if (_keeper == address(0)) revert ZeroAddress(); _grantRole(DEFAULT_ADMIN_ROLE, _admin); _grantRole(KEEPER_ROLE, _keeper); } event TreasuryUpdated(address indexed user, address newTreasury); event PerformanceFeeUpdated(address indexed user, uint256 newPerformanceFee); event FloatPercentageUpdated(address indexed user, uint256 newFloatPercentage); event SlippageToleranceUpdated(address indexed admin, uint256 newSlippageTolerance); /// Role allowed to harvest/reinvest bytes32 public constant KEEPER_ROLE = keccak256("KEEPER_ROLE"); // flag for checking flash loan caller bool public flashLoanInitiated; // address of the treasury to send performance fees to address public treasury; // performance fee percentage uint256 public performanceFee = 0.1e18; // 10% // percentage of the total assets to be kept in the vault as a withdrawal buffer uint256 public floatPercentage = 0.01e18; // max slippage tolerance for swaps uint256 public slippageTolerance = 0.99e18; // 1% default /// @notice set the treasury address /// @param _newTreasury the new treasury address function setTreasury(address _newTreasury) external { _onlyAdmin(); if (_newTreasury == address(0)) revert TreasuryCannotBeZero(); treasury = _newTreasury; emit TreasuryUpdated(msg.sender, _newTreasury); } /// @notice set the performance fee percentage /// @param _newPerformanceFee the new performance fee percentage /// @dev performance fee is a number between 0 and 1e18 function setPerformanceFee(uint256 _newPerformanceFee) external { _onlyAdmin(); if (_newPerformanceFee > 1e18) revert FeesTooHigh(); performanceFee = _newPerformanceFee; emit PerformanceFeeUpdated(msg.sender, _newPerformanceFee); } /** * @notice Set the percentage of the total assets to be kept in the vault as a withdrawal buffer. * @param _newFloatPercentage The new float percentage value. */ function setFloatPercentage(uint256 _newFloatPercentage) external { _onlyAdmin(); if (_newFloatPercentage > C.ONE) revert InvalidFloatPercentage(); floatPercentage = _newFloatPercentage; emit FloatPercentageUpdated(msg.sender, _newFloatPercentage); } /** * @notice Set the default slippage tolerance for swapping tokens. * @param _newSlippageTolerance The new slippage tolerance value. */ function setSlippageTolerance(uint256 _newSlippageTolerance) external { _onlyAdmin(); if (_newSlippageTolerance > C.ONE) revert InvalidSlippageTolerance(); slippageTolerance = _newSlippageTolerance; emit SlippageToleranceUpdated(msg.sender, _newSlippageTolerance); } function _onlyAdmin() internal view { if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) revert CallerNotAdmin(); } function _onlyKeeper() internal view { if (!hasRole(KEEPER_ROLE, msg.sender)) revert CallerNotKeeper(); } function _onlyKeeperOrFlashLoan() internal view { if (!flashLoanInitiated) _onlyKeeper(); } function _initiateFlashLoan() internal { flashLoanInitiated = true; } function _finalizeFlashLoan() internal { flashLoanInitiated = false; } function _isFlashLoanInitiated() internal view { if (!flashLoanInitiated) revert InvalidFlashLoanCaller(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol) pragma solidity ^0.8.0; import "./IAccessControl.sol"; import "../utils/Context.sol"; import "../utils/Strings.sol"; import "../utils/introspection/ERC165.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ``` * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ``` * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. */ abstract contract AccessControl is Context, IAccessControl, ERC165 { struct RoleData { mapping(address => bool) members; bytes32 adminRole; } mapping(bytes32 => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with a standardized message including the required role. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ * * _Available since v4.1._ */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual override returns (bool) { return _roles[role].members[account]; } /** * @dev Revert with a standard message if `_msgSender()` is missing `role`. * Overriding this function changes the behavior of the {onlyRole} modifier. * * Format of the revert message is described in {_checkRole}. * * _Available since v4.6._ */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, _msgSender()); } /** * @dev Revert with a standard message if `account` is missing `role`. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert( string( abi.encodePacked( "AccessControl: account ", Strings.toHexString(account), " is missing role ", Strings.toHexString(uint256(role), 32) ) ) ); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleGranted} event. */ function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleRevoked} event. */ function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. * * May emit a {RoleRevoked} event. */ function renounceRole(bytes32 role, address account) public virtual override { require(account == _msgSender(), "AccessControl: can only renounce roles for self"); _revokeRole(role, account); } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. Note that unlike {grantRole}, this function doesn't perform any * checks on the calling account. * * May emit a {RoleGranted} event. * * [WARNING] * ==== * This function should only be called from the constructor when setting * up the initial roles for the system. * * Using this function in any other way is effectively circumventing the admin * system imposed by {AccessControl}. * ==== * * NOTE: This function is deprecated in favor of {_grantRole}. */ function _setupRole(bytes32 role, address account) internal virtual { _grantRole(role, account); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Grants `role` to `account`. * * Internal function without access restriction. * * May emit a {RoleGranted} event. */ function _grantRole(bytes32 role, address account) internal virtual { if (!hasRole(role, account)) { _roles[role].members[account] = true; emit RoleGranted(role, account, _msgSender()); } } /** * @dev Revokes `role` from `account`. * * Internal function without access restriction. * * May emit a {RoleRevoked} event. */ function _revokeRole(bytes32 role, address account) internal virtual { if (hasRole(role, account)) { _roles[role].members[account] = false; emit RoleRevoked(role, account, _msgSender()); } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.10; import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol"; interface IwstETH is IERC20 { function wrap(uint256 _stETHAmount) external returns (uint256); /** * @notice Exchanges wstETH to stETH * @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH * @dev Requirements: * - `_wstETHAmount` must be non-zero * - msg.sender must have at least `_wstETHAmount` wstETH. * @return Amount of stETH user receives after unwrap */ function unwrap(uint256 _wstETHAmount) external returns (uint256); /** * @notice Get amount of wstETH for a given amount of stETH * @param _stETHAmount amount of stETH * @return Amount of wstETH for a given stETH amount */ function getWstETHByStETH(uint256 _stETHAmount) external view returns (uint256); /** * @notice Get amount of stETH for a given amount of wstETH * @param _wstETHAmount amount of wstETH * @return Amount of stETH for a given wstETH amount */ function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256); /** * @notice Get amount of stETH for a one wstETH * @return Amount of stETH for 1 wstETH */ function stEthPerToken() external view returns (uint256); /** * @notice Get amount of wstETH for a one stETH * @return Amount of wstETH for a 1 stETH */ function tokensPerStEth() external view returns (uint256); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.10; import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol"; interface ILido is IERC20 { function submit(address _referral) external payable returns (uint256); /** * @return the entire amount of Ether controlled by the protocol. * * @dev The sum of all ETH balances in the protocol, equals to the total supply of stETH. */ function getTotalPooledEther() external view returns (uint256); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.10; interface ICurvePool { function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external payable returns (uint256); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.13; /// @title Router token swapping functionality /// @notice Functions for swapping tokens via Uniswap V3 interface ISwapRouter { struct ExactInputSingleParams { address tokenIn; address tokenOut; uint24 fee; address recipient; uint256 deadline; uint256 amountIn; uint256 amountOutMinimum; uint160 sqrtPriceLimitX96; } /// @notice Swaps `amountIn` of one token for as much as possible of another token /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata /// @return amountOut The amount of the received token function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut); struct ExactOutputSingleParams { address tokenIn; address tokenOut; uint24 fee; address recipient; uint256 deadline; uint256 amountOut; uint256 amountInMaximum; uint160 sqrtPriceLimitX96; } /// @notice Swaps as little as possible of one token for `amountOut` of another token /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata /// @return amountIn The amount of the input token function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) pragma solidity ^0.8.0; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControl { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// 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 (last updated v4.8.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv( uint256 x, uint256 y, uint256 denominator, Rounding rounding ) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10**64) { value /= 10**64; result += 64; } if (value >= 10**32) { value /= 10**32; result += 32; } if (value >= 10**16) { value /= 10**16; result += 16; } if (value >= 10**8) { value /= 10**8; result += 8; } if (value >= 10**4) { value /= 10**4; result += 4; } if (value >= 10**2) { value /= 10**2; result += 2; } if (value >= 10**1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
{ "remappings": [ "create3-factory/=lib/create3-factory/src/", "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "solmate/=lib/solmate/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/", "aave-v3/=lib/aave-v3-core/contracts/", "surl/=lib/surl/src/", "aave-v3-core/=lib/aave-v3-core/", "euler-interfaces/=lib/euler-interfaces/contracts/", "solidity-stringutils/=lib/surl/lib/solidity-stringutils/" ], "optimizer": { "enabled": true, "runs": 1000000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address","name":"_keeper","type":"address"},{"internalType":"contract ERC4626","name":"_scWETH","type":"address"},{"internalType":"contract PriceConverter","name":"_priceConverter","type":"address"},{"internalType":"contract Swapper","name":"_swapper","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CallerNotAdmin","type":"error"},{"inputs":[],"name":"CallerNotKeeper","type":"error"},{"inputs":[],"name":"EndUsdcBalanceTooLow","type":"error"},{"inputs":[],"name":"FeesTooHigh","type":"error"},{"inputs":[],"name":"FlashLoanAmountZero","type":"error"},{"inputs":[{"internalType":"uint256","name":"actual","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"FloatBalanceTooLow","type":"error"},{"inputs":[],"name":"InvalidFlashLoanCaller","type":"error"},{"inputs":[],"name":"InvalidFloatPercentage","type":"error"},{"inputs":[],"name":"InvalidSlippageTolerance","type":"error"},{"inputs":[],"name":"NoProfitsToSell","type":"error"},{"inputs":[{"internalType":"uint256","name":"adapterId","type":"uint256"}],"name":"ProtocolInUse","type":"error"},{"inputs":[{"internalType":"uint256","name":"adapterId","type":"uint256"}],"name":"ProtocolNotSupported","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"TokenOutNotAllowed","type":"error"},{"inputs":[],"name":"TreasuryCannotBeZero","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"adapterId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Borrowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"wethAmount","type":"uint256"}],"name":"Disinvested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"wethWithdrawn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"debtRepaid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralReleased","type":"uint256"}],"name":"EmergencyExitExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"newFloatPercentage","type":"uint256"}],"name":"FloatPercentageUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"wethAmount","type":"uint256"}],"name":"Invested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"newPerformanceFee","type":"uint256"}],"name":"PerformanceFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"wethSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"usdcReceived","type":"uint256"}],"name":"ProfitSold","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"adapterId","type":"uint256"},{"indexed":false,"internalType":"address","name":"adapter","type":"address"}],"name":"ProtocolAdapterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"adapterId","type":"uint256"}],"name":"ProtocolAdapterRemoved","type":"event"},{"anonymous":false,"inputs":[],"name":"Reallocated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"totalCollateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalDebt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"floatBalance","type":"uint256"}],"name":"Rebalanced","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"adapterId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Repaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"adapterId","type":"uint256"}],"name":"RewardsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"newSlippageTolerance","type":"uint256"}],"name":"SlippageToleranceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"adapterId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Supplied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"address","name":"newSwapper","type":"address"}],"name":"SwapperUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountReceived","type":"uint256"}],"name":"TokenSwapped","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"TokenWhitelisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"newTreasury","type":"address"}],"name":"TreasuryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"adapterId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KEEPER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IAdapter","name":"_adapter","type":"address"}],"name":"addAdapter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"balancerVault","outputs":[{"internalType":"contract IVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"},{"internalType":"bytes","name":"_callData","type":"bytes"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"disinvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_endUsdcBalanceMin","type":"uint256"}],"name":"exitAllPositions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"flashLoanInitiated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"floatPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"}],"name":"getCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"}],"name":"getDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProfit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"}],"name":"isSupported","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"_token","type":"address"}],"name":"isTokenWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"performanceFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceConverter","outputs":[{"internalType":"contract PriceConverter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_flashLoanAmount","type":"uint256"},{"internalType":"bytes[]","name":"_callData","type":"bytes[]"}],"name":"reallocate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"_callData","type":"bytes[]"}],"name":"rebalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"_feeAmounts","type":"uint256[]"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"receiveFlashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"},{"internalType":"bool","name":"_force","type":"bool"}],"name":"removeAdapter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"repay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"scWETH","outputs":[{"internalType":"contract ERC4626","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_usdcAmountOutMin","type":"uint256"}],"name":"sellProfit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newFloatPercentage","type":"uint256"}],"name":"setFloatPercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newPerformanceFee","type":"uint256"}],"name":"setPerformanceFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newSlippageTolerance","type":"uint256"}],"name":"setSlippageTolerance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Swapper","name":"_newSwapper","type":"address"}],"name":"setSwapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newTreasury","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"slippageTolerance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"supply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapper","outputs":[{"internalType":"contract Swapper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCollateral","outputs":[{"internalType":"uint256","name":"total","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDebt","outputs":[{"internalType":"uint256","name":"total","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"usdcBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"contract WETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wethInvested","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"_token","type":"address"},{"internalType":"bool","name":"_value","type":"bool"}],"name":"whiteListOutToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_adapterId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"_tokenIn","type":"address"},{"internalType":"contract ERC20","name":"_tokenOut","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_swapData","type":"bytes"},{"internalType":"uint256","name":"_assetAmountOutMin","type":"uint256"}],"name":"zeroExSwap","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
61014060405267016345785d8a0000600855662386f26fc10000600955670dbd2fc137a30000600a553480156200003557600080fd5b5060405162006360380380620063608339810160408190526200005891620004b3565b848473a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4884846040518060400160405280601481526020017f53616e64636c6f636b205969656c6420555344430000000000000000000000008152506040518060400160405280600681526020016573635553444360d01b81525086868684848282828181846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200010f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000135919062000533565b600062000143848262000604565b50600162000152838262000604565b5060ff81166080524660a05262000168620002af565b60c0525050506001600160a01b0392831660e052505085166200019e5760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038416620001c65760405163d92e233d60e01b815260040160405180910390fd5b620001d36000866200034b565b620001ff7ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab856200034b565b50505050506200021584620003f060201b60201c565b6200022083620003f0565b50506001600160a01b0391821661010052600b80546001600160a01b031916918316919091179055166000908152600f60205260409020805460ff191660011790555062000270905083620003f0565b6001600160a01b038316610120819052620002a49073c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2906000196200041b565b50505050506200074e565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051620002e39190620006d0565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b60008281526006602090815260408083206001600160a01b038516845290915290205460ff16620003ec5760008281526006602090815260408083206001600160a01b03851684529091529020805460ff19166001179055620003ab3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b6001600160a01b038116620004185760405163d92e233d60e01b815260040160405180910390fd5b50565b600060405163095ea7b360e01b8152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080620004975760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b604482015260640160405180910390fd5b50505050565b6001600160a01b03811681146200041857600080fd5b600080600080600060a08688031215620004cc57600080fd5b8551620004d9816200049d565b6020870151909550620004ec816200049d565b6040870151909450620004ff816200049d565b606087015190935062000512816200049d565b608087015190925062000525816200049d565b809150509295509295909350565b6000602082840312156200054657600080fd5b815160ff811681146200055857600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200058a57607f821691505b602082108103620005ab57634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620005ff57600081815260208120601f850160051c81016020861015620005da5750805b601f850160051c820191505b81811015620005fb57828155600101620005e6565b5050505b505050565b81516001600160401b038111156200062057620006206200055f565b620006388162000631845462000575565b84620005b1565b602080601f831160018114620006705760008415620006575750858301515b600019600386901b1c1916600185901b178555620005fb565b600085815260208120601f198616915b82811015620006a15788860151825594840194600190910190840162000680565b5085821015620006c05787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6000808354620006e08162000575565b60018281168015620006fb5760018114620007115762000742565b60ff198416875282151583028701945062000742565b8760005260208060002060005b85811015620007395781548a8201529084019082016200071e565b50505082870194505b50929695505050505050565b60805160a05160c05160e0516101005161012051615b596200080760003960008181610996015281816112f001528181611ca0015281816137e801528181613ca70152613d69015260008181610a2f015281816131870152818161321b01526140280152600081816106f701528181611ab101528181611bee01528181611d7a01528181612636015281816128b001528181613ac70152614336015260006110d2015260006110a20152600061067c0152615b596000f3fe608060405234801561001057600080fd5b50600436106104f95760003560e01c806373688e56116102a1578063b93855101161016b578063d505accf116100e3578063dd62ed3e11610097578063f04f27071161007c578063f04f270714610b8c578063f0f4426014610b9f578063fc7b9c1814610bb257600080fd5b8063dd62ed3e14610b4e578063ef8b30f714610b7957600080fd5b8063d8aed145116100c8578063d8aed14514610af2578063d905777e14610b05578063daecfa6c14610b3b57600080fd5b8063d505accf14610acc578063d547741f14610adf57600080fd5b8063c7e106d81161013a578063c9dd1f3c1161011f578063c9dd1f3c14610a9d578063ce96cb7714610ab0578063d03153aa14610ac357600080fd5b8063c7e106d814610a77578063c923299a14610a8a57600080fd5b8063b938551014610a2a578063ba08765214610a51578063c63d75b61461075a578063c6e6f59214610a6457600080fd5b80639ceffcde11610219578063b0bfa1b3116101cd578063b460af94116101b2578063b460af94146109cb578063b5af090f146109de578063b5ba383314610a1757600080fd5b8063b0bfa1b314610991578063b3d7f6b9146109b857600080fd5b8063a217fddf116101fe578063a217fddf14610963578063a34130ae1461096b578063a9059cbb1461097e57600080fd5b80639ceffcde1461093d5780639ef009061461095057600080fd5b80638d60cded1161027057806394bf804d1161025557806394bf804d1461090f57806395d89b41146109225780639c82f2a41461092a57600080fd5b80638d60cded146108b657806391d14854146108c957600080fd5b806373688e561461087d578063773a6c4f146108855780637ecebe001461088d57806387788782146108ad57600080fd5b806336568abe116103e2578063549dd8c31161035a578063679dffb41161030e5780636e553f65116102f35780636e553f651461083757806370897b231461084a57806370a082311461085d57600080fd5b8063679dffb41461081c5780636860e9d21461082457600080fd5b806360d54d411161033f57806360d54d41146107d757806361d027b3146107ea5780636457f7551461080f57600080fd5b8063549dd8c3146107bb5780635892457d146107ce57600080fd5b80633fc8cef3116103b1578063441a3e7011610396578063441a3e701461078d5780634ac8eb5f146107a05780634cdad506146107a857600080fd5b80633fc8cef31461073f578063402d267d1461075a57600080fd5b806336568abe146106df57806338d52e0f146106f2578063392179c6146107195780633c0393821461072c57600080fd5b806318160ddd116104755780632b3297f911610444578063313ce56711610429578063313ce567146106775780633644e515146106b0578063364bc15a146106b857600080fd5b80632b3297f9146106445780632f2ff15d1461066457600080fd5b806318160ddd146105f257806323b872dd146105fb578063248a9ca31461060e5780632a62a4901461063157600080fd5b8063095ea7b3116104cc5780630ecbcdab116104b15780630ecbcdab1461058a578063117da1ee1461059f578063158274a5146105b257600080fd5b8063095ea7b3146105645780630a28a4771461057757600080fd5b806301e1d114146104fe57806301ffc9a71461051957806306fdde031461053c57806307a2d13a14610551575b600080fd5b610506610bba565b6040519081526020015b60405180910390f35b61052c610527366004614d56565b610be9565b6040519015158152602001610510565b610544610c82565b6040516105109190614e06565b61050661055f366004614e19565b610d10565b61052c610572366004614e54565b610d3d565b610506610585366004614e19565b610db6565b61059d610598366004614e80565b610dd6565b005b61059d6105ad366004614e19565b610df5565b6105cd73ba12222222228d8ba445958a75a0704d566bf2c881565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610510565b61050660025481565b61052c610609366004614ea2565b610e7d565b61050661061c366004614e19565b60009081526006602052604090206001015490565b61050661063f366004614e19565b610fc1565b600b546105cd9073ffffffffffffffffffffffffffffffffffffffff1681565b61059d610672366004614ee3565b611074565b61069e7f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610510565b61050661109e565b6105067ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab81565b61059d6106ed366004614ee3565b6110f4565b6105cd7f000000000000000000000000000000000000000000000000000000000000000081565b61059d610727366004614f58565b6111a8565b61059d61073a366004614e19565b611289565b6105cd73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b610506610768366004614f9a565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90565b61059d61079b366004614e80565b61166d565b610506611688565b6105066107b6366004614e19565b611766565b61059d6107c9366004614ff9565b611771565b61050660095481565b61059d6107e5366004614f9a565b61185c565b6007546105cd90610100900473ffffffffffffffffffffffffffffffffffffffff1681565b60075461052c9060ff1681565b6105066119f5565b61059d610832366004614e19565b611a0f565b610506610845366004614ee3565b611a20565b61059d610858366004614e19565b611b3c565b61050661086b366004614f9a565b60036020526000908152604090205481565b610506611bbd565b610506611c6f565b61050661089b366004614f9a565b60056020526000908152604090205481565b61050660085481565b61052c6108c4366004614e19565b611d46565b61052c6108d7366004614ee3565b600091825260066020908152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b61050661091d366004614ee3565b611d53565b610544611dfc565b61059d610938366004614f9a565b611e09565b61059d61094b36600461505a565b611ed3565b61059d61095e366004615086565b612066565b610506600081565b61059d610979366004615101565b612220565b61052c61098c366004614e54565b61244c565b6105cd7f000000000000000000000000000000000000000000000000000000000000000081565b6105066109c6366004614e19565b6124d1565b6105066109d9366004615140565b6124f0565b61052c6109ec366004614f9a565b73ffffffffffffffffffffffffffffffffffffffff166000908152600f602052604090205460ff1690565b61059d610a25366004614e19565b61265d565b6105cd7f000000000000000000000000000000000000000000000000000000000000000081565b610506610a5f366004615140565b612700565b610506610a72366004614e19565b6128d7565b61059d610a85366004615182565b6128f7565b61059d610a98366004614e19565b6129db565b610506610aab366004614e19565b612a5c565b610506610abe366004614f9a565b612ad2565b610506600a5481565b61059d610ada3660046151ae565b612b01565b61059d610aed366004614ee3565b612e20565b61059d610b00366004614e80565b612e45565b610506610b13366004614f9a565b73ffffffffffffffffffffffffffffffffffffffff1660009081526003602052604090205490565b61059d610b49366004614e80565b612e60565b610506610b5c366004615225565b600460209081526000928352604080842090915290825290205481565b610506610b87366004614e19565b612e7b565b61059d610b9a366004615253565b612e86565b61059d610bad366004614f9a565b612f6f565b610506613040565b6000610be4610bc7611bbd565b610bcf611688565b610bd7611c6f565b610bdf613040565b613119565b905090565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b000000000000000000000000000000000000000000000000000000001480610c7c57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60008054610c8f90615317565b80601f0160208091040260200160405190810160405280929190818152602001828054610cbb90615317565b8015610d085780601f10610cdd57610100808354040283529160200191610d08565b820191906000526020600020905b815481529060010190602001808311610ceb57829003601f168201915b505050505081565b6002546000908015610d3457610d2f610d27610bba565b8490836132bf565b610d36565b825b9392505050565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610da59086815260200190565b60405180910390a350600192915050565b6002546000908015610d3457610d2f81610dce610bba565b8591906132fb565b610dde61333f565b610de782613353565b610df18282613398565b5050565b610dfd6133ed565b670de0b6b3a7640000811115610e3f576040517fc31c0b6e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a81905560405181815233907fb3a17cf8bbe3a4348266beede3a365af15dd59203021a6c1121d00ca2d5019f7906020015b60405180910390a250565b73ffffffffffffffffffffffffffffffffffffffff831660009081526004602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610f1157610edf8382615399565b73ffffffffffffffffffffffffffffffffffffffff861660009081526004602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff851660009081526003602052604081208054859290610f46908490615399565b909155505073ffffffffffffffffffffffffffffffffffffffff808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90610fae9087815260200190565b60405180910390a3506001949350505050565b6000610fcc82611d46565b610fd857506000919050565b610fe3600c83613455565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639b56d6c9906024015b602060405180830381865afa158015611050573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c7c91906153ac565b60008281526006602052604090206001015461108f81613461565b611099838361346b565b505050565b60007f000000000000000000000000000000000000000000000000000000000000000046146110cf57610be461355f565b507f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff8116331461119e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b610df182826135f9565b6111b06136b4565b6111c26111bd8284615537565b61371c565b6111ca6137a1565b60006111d4611bbd565b905060006111ec6009546111e6610bba565b906138a1565b905080821015611232576040517fcd62da430000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401611195565b7f83387a3342ff1ebc5e437dc9ae0f98274afda12a11cf547eebec05a3e0b8f8a761125b611688565b611263613040565b60408051928352602083019190915281018490526060015b60405180910390a150505050565b6112916136b4565b600061129b611688565b905060006112a7613040565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290915060009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063ba0876529082906370a0823190602401602060405180830381865afa15801561133f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061136391906153ac565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526004810191909152306024820181905260448201526064016020604051808303816000875af11580156113c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ea91906153ac565b9050808211156115ba576040805160018082528183019092526000916020808301908036833701905050905073c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28160008151811061143e5761143e615544565b73ffffffffffffffffffffffffffffffffffffffff92909216602092830291909101909101526040805160018082528183019092526000918160200160208202803683370190505090506114928385615399565b816000815181106114a5576114a5615544565b6020026020010181815250506114e1600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b73ba12222222228d8ba445958a75a0704d566bf2c873ffffffffffffffffffffffffffffffffffffffff16635c38449e308484600160405160200161152691906155dd565b6040516020818303038152906040526040518563ffffffff1660e01b815260040161155494939291906155eb565b600060405180830381600087803b15801561156e57600080fd5b505af1158015611582573d6000803e3d6000fd5b505050506115b3600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b50506115e3565b6115c26138b6565b60006115cc613a40565b905080156115e1576115df816000613a93565b505b505b836115ec611bbd565b1015611624576040517f80fece5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518281526020810184905290810184905233907f8f8ca188bc27143e106193f8f078b05437841134a613ade83e2642758c0e09009060600160405180910390a250505050565b61167561333f565b61167e82613353565b610df18282613b53565b600080611695600c613ba8565b905060005b818110156117615760006116af600c83613bb3565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015290925073ffffffffffffffffffffffffffffffffffffffff83169150639b56d6c990602401602060405180830381865afa15801561171d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061174191906153ac565b61174b908561569e565b9350508080611759906156b1565b91505061169a565b505090565b6000610c7c82610d10565b61177961333f565b61178283613353565b6118238363e190febc60e01b84846040516024016117a1929190615732565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613bd1565b6040518381527fed6771ea2f01816faa7ccf034b76201581d0b4374e86f83b06a8b4191c6b9f89906020015b60405180910390a1505050565b6118646133ed565b60008173ffffffffffffffffffffffffffffffffffffffff1663af640d0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118d591906153ac565b90506118e081611d46565b1561191a576040517f254de06900000000000000000000000000000000000000000000000000000000815260048101829052602401611195565b611926600c8284613bfc565b506040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f8757b15b0000000000000000000000000000000000000000000000000000000017905261199f9073ffffffffffffffffffffffffffffffffffffffff841690613c1f565b506040805182815273ffffffffffffffffffffffffffffffffffffffff8416602082015233917f7ae8ce35a1794068c85a72f63d16209787d9e4f3e4b3095cf7f0e1de1e5e074191015b60405180910390a25050565b6000610be4611a02611c6f565b611a0a613040565b613c44565b611a176136b4565b610df181613c5e565b6000611a2b83612e7b565b905080600003611a97576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f5348415245530000000000000000000000000000000000000000006044820152606401611195565b611ad973ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016333086613e18565b611ae38282613ede565b604080518481526020810183905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d791015b60405180910390a3610c7c565b611b446133ed565b670de0b6b3a7640000811115611b86576040517fc9034e1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600881905560405181815233907f48d9d467a6699d3c9f09688328d42ebb56b0d1598687d06bebbe2e7f4264156290602001610e72565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a08231906024015b602060405180830381865afa158015611c4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be491906153ac565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906307a2d13a9082906370a0823190602401602060405180830381865afa158015611d04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d2891906153ac565b6040518263ffffffff1660e01b8152600401611c2e91815260200190565b6000610c7c600c83613f57565b6000611d5e836124d1565b9050611da273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016333084613e18565b611dac8284613ede565b604080518281526020810185905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d79101611b2f565b60018054610c8f90615317565b611e116133ed565b73ffffffffffffffffffffffffffffffffffffffff8116611e5e576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560405190815233907ffb7a5f1d35a7022d9d6343bfc9a25035829d0ea72da06978793c945b1d94a17f90602001610e72565b611edb6133ed565b611ee482613353565b80158015611f8c57506000611efa600c84613455565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639b56d6c990602401602060405180830381865afa158015611f66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f8a91906153ac565b115b15611fc6576040517f254de06900000000000000000000000000000000000000000000000000000000815260048101839052602401611195565b6040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9265a7d500000000000000000000000000000000000000000000000000000000179052612028908390613bd1565b612033600c83613f63565b5060405182815233907fce27e7409a7e5bc40ecb15d4e938f30ecc5e1e44b03b8f9517eb0a3bf906eccc906020016119e9565b61206e61333f565b73ffffffffffffffffffffffffffffffffffffffff85166000908152600f602052604090205460ff166120e5576040517fcc2342f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86166004820152602401611195565b60006121a9631f2913d260e01b88888886898960405160240161210d96959493929190615746565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152600b5473ffffffffffffffffffffffffffffffffffffffff1690613c1f565b90507fd5a1cd88ddd329cc1ddf861a21708efa9ef3136b88e1420c21f91ef6f95ad0248786838060200190518101906121e291906153ac565b6040805173ffffffffffffffffffffffffffffffffffffffff909416845260208401929092529082015260600160405180910390a150505050505050565b6122286136b4565b82600003612262576040517f667000d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160018082528183019092526000916020808301908036833701905050905073c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2816000815181106122ac576122ac615544565b73ffffffffffffffffffffffffffffffffffffffff9290921660209283029190910190910152604080516001808252818301909252600091816020016020820280368337019050509050848160008151811061230a5761230a615544565b602002602001018181525050612346600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b73ba12222222228d8ba445958a75a0704d566bf2c873ffffffffffffffffffffffffffffffffffffffff16635c38449e3084846000898960405160200161238f93929190615798565b6040516020818303038152906040526040518563ffffffff1660e01b81526004016123bd94939291906155eb565b600060405180830381600087803b1580156123d757600080fd5b505af11580156123eb573d6000803e3d6000fd5b5050505061241c600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b6040517f9beffa21c10379adc18939ddf9613b794f42e714f3ef783faae5d011d0389e5590600090a15050505050565b3360009081526003602052604081208054839190839061246d908490615399565b909155505073ffffffffffffffffffffffffffffffffffffffff8316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90610da59086815260200190565b6002546000908015610d3457610d2f6124e8610bba565b8490836132fb565b60006124fb84610db6565b90503373ffffffffffffffffffffffffffffffffffffffff8316146125b05773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146125ae5761257c8282615399565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b6125ba8482613f6f565b6125c482826140cc565b604080518581526020810183905273ffffffffffffffffffffffffffffffffffffffff808516929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a4610d3673ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016848661415a565b6126656136b4565b6000612672611a02611c6f565b9050806000036126ae576040517f6733d0e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006126b982613c5e565b905060006126c78285613a93565b60408051848152602081018390529192507fa0b83a013fc7994bd0e79a69e2adb2ddda2cb17acf657a73120900b26edcd206910161127b565b60003373ffffffffffffffffffffffffffffffffffffffff8316146127b55773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146127b3576127818582615399565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b6127be84611766565b90508060000361282a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f4153534554530000000000000000000000000000000000000000006044820152606401611195565b6128348185613f6f565b61283e82856140cc565b604080518281526020810186905273ffffffffffffffffffffffffffffffffffffffff808516929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a4610d3673ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016848361415a565b6002546000908015610d3457610d2f816128ef610bba565b8591906132bf565b6128ff6133ed565b73ffffffffffffffffffffffffffffffffffffffff821661294c576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000818152600f602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168515159081179091558251938452908301527fef81a9943b96c8df4ef243401c9bf5159146166211356898b52d382086168d9291015b60405180910390a15050565b6129e36133ed565b670de0b6b3a7640000811115612a25576040517f7bfb537900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600981905560405181815233907fb414942c66de9d94d6f12b7818ecb6c326ee7f58980297cfba9a54b979e01d3390602001610e72565b6000612a6782611d46565b612a7357506000919050565b612a7e600c83613455565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639a78e72e90602401611033565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260036020526040812054610c7c90610d10565b42841015612b6b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401611195565b60006001612b7761109e565b73ffffffffffffffffffffffffffffffffffffffff8a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015612cc9573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590612d4457508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b612daa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152606401611195565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b600082815260066020526040902060010154612e3b81613461565b61109983836135f9565b612e4d61333f565b612e5682613353565b610df18282614219565b612e6861333f565b612e7182613353565b610df1828261428b565b6000610c7c826128d7565b612e8e6142e0565b600086866000818110612ea357612ea3615544565b90506020020135905060008383810190612ebd9190615891565b90506001816001811115612ed357612ed3615573565b03612eee57612ee06138b6565b612ee98261431c565b612f0a565b6000612efc848601866158ac565b915050612f088161371c565b505b612f6373ba12222222228d8ba445958a75a0704d566bf2c887876000818110612f3557612f35615544565b9050602002013584612f47919061569e565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2919061415a565b50505050505050505050565b612f776133ed565b73ffffffffffffffffffffffffffffffffffffffff8116612fc4576040517f91f7acdb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600780547fffffffffffffffffffffff0000000000000000000000000000000000000000ff1661010073ffffffffffffffffffffffffffffffffffffffff84169081029190911790915560405190815233907f4ab5be82436d353e61ca18726e984e561f5c1cc7c6d38b29d2553c790434705a90602001610e72565b60008061304d600c613ba8565b905060005b81811015611761576000613067600c83613bb3565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015290925073ffffffffffffffffffffffffffffffffffffffff83169150639a78e72e90602401602060405180830381865afa1580156130d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130f991906153ac565b613103908561569e565b9350508080613111906156b1565b915050613052565b6000613125848661569e565b905060006131338484613c44565b9050801561320457600a546040517fd8bddbd8000000000000000000000000000000000000000000000000000000008152600481018390526131f3919073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063d8bddbd8906024015b602060405180830381865afa1580156131cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e691906153ac565b6131fd908361569e565b91506132b6565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663d8bddbd861324a8686615399565b6040518263ffffffff1660e01b815260040161326891815260200190565b602060405180830381865afa158015613285573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132a991906153ac565b6132b39083615399565b91505b50949350505050565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04841183021582026132f457600080fd5b5091020490565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048411830215820261333057600080fd5b50910281810615159190040190565b60075460ff16613351576133516136b4565b565b61335c81611d46565b613395576040517f10028b5400000000000000000000000000000000000000000000000000000000815260048101829052602401611195565b50565b6133b78263c5ebeaec60e01b836040516024016117a191815260200190565b60408051838152602081018390527f14a13f958d7fe38fb8529e8d43353b6d9120f52444e486a4077f68a3fc77dcbf91016129cf565b3360009081527f54cdd369e4e8a8515e52ca72ec816c2101831ad1f18bf44102ed171459c9b4f8602052604090205460ff16613351576040517f06d919f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610d3683836143d8565b6133958133614462565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16610df157600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff85168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556135013390565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051613591919061590e565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff1615610df157600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b3360009081527f102c8bff359ba7f792f3597c956360de34af36b3eee541f0c28549f821bb5369602052604090205460ff16613351576040517f04e1fa9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8151811015610df15781818151811061373a5761373a615544565b6020026020010151516000031561378f5761378d82828151811061376057613760615544565b60200260200101513073ffffffffffffffffffffffffffffffffffffffff16613c1f90919063ffffffff16565b505b80613799816156b1565b91505061371f565b60006137ab613a40565b90508015613395576040517f6e553f65000000000000000000000000000000000000000000000000000000008152600481018290523060248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690636e553f65906044016020604051808303816000875af1158015613846573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061386a91906153ac565b506040518181527fac9f7997c30b6a3cc9c74953898b5de154359339c7ec0d6d70ceb98e55db1a4b9060200160405180910390a150565b6000610d368383670de0b6b3a76400006132bf565b60006138c2600c613ba8565b905060005b81811015610df1576000806138dd600c84613bb3565b6040517f9a78e72e000000000000000000000000000000000000000000000000000000008152306004820152919350915060009073ffffffffffffffffffffffffffffffffffffffff831690639a78e72e90602401602060405180830381865afa15801561394f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061397391906153ac565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015290915060009073ffffffffffffffffffffffffffffffffffffffff841690639b56d6c990602401602060405180830381865afa1580156139e3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a0791906153ac565b90508115613a1957613a198483614219565b8015613a2957613a298482613b53565b505050508080613a38906156b1565b9150506138c7565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2906370a0823190602401611c2e565b60405173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2602482015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016604482015260648101839052608481018290526101f460a48201526000908190613b35907f0db634ba000000000000000000000000000000000000000000000000000000009060c40161210d565b905080806020019051810190613b4b91906153ac565b949350505050565b613b7282632e1a7d4d60e01b836040516024016117a191815260200190565b60408051838152602081018390527f0c875c8d391179c5cf7ad8303d268efd50b8beb78b671f85cd54bfb91eb8ef4091016129cf565b6000610c7c8261451c565b6000808080613bc28686614527565b909450925050505b9250929050565b61109981613be0600c85613455565b73ffffffffffffffffffffffffffffffffffffffff1690613c1f565b6000613b4b848473ffffffffffffffffffffffffffffffffffffffff8516614552565b6060610d368383604051806060016040528060278152602001615afd6027913961456f565b6000818311613c54576000610d36565b610d368284615399565b6040517fc6e6f59200000000000000000000000000000000000000000000000000000000815260048101829052600090819073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063c6e6f59290602401602060405180830381865afa158015613cee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d1291906153ac565b6040517fba087652000000000000000000000000000000000000000000000000000000008152600481018290523060248201819052604482015290915060009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063ba087652906064016020604051808303816000875af1158015613db2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dd691906153ac565b90507ff7e72e2f53a982a67fc999c00edfd87a94bdf99e5031c74851276cfd65b0bd2981604051613e0991815260200190565b60405180910390a19392505050565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080613ed7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401611195565b5050505050565b8060026000828254613ef0919061569e565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b6000610d3683836145f4565b6000610d368383614600565b6000613f79611bbd565b9050828110613f8757505050565b6000613f91611688565b90506000613f9d613040565b90506000613fa9611c6f565b90506000613fb985858486613119565b90506000613fc78385613c44565b90506000888311613fd9576000613ff0565b600954613ff090613fea8b86615399565b9061461d565b9050600087613fff838c61569e565b6140099190615399565b905082156140c057600061401c84613c5e565b90506000614081600a547f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d8bddbd8856040518263ffffffff1660e01b81526004016131b291815260200190565b9050600061408f8383613a93565b90508c61409c828d61569e565b106140b05750505050505050505050505050565b6140ba8185615399565b93505050505b612f6386888784614632565b73ffffffffffffffffffffffffffffffffffffffff821660009081526003602052604081208054839290614101908490615399565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001613f4b565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080614213576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401611195565b50505050565b6000614223613a40565b90508082116142325781614234565b805b91506142558363371fd8e660e01b846040516024016117a191815260200190565b60408051848152602081018490527f81472a96709c8315c82af40d41ef624a642ad53864b097e53af675593bb4e035910161184f565b6142aa82633540302360e01b836040516024016117a191815260200190565b60408051838152602081018390527f3b81a6e0eae880a578a3b45b9345aa6c424b285b830d9a4037b63f50b8380f4391016129cf565b60075460ff16613351576040517fe17c49b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016602482015273c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26044820152606481018290527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60848201526101f460a4820152610df1907f4ad44e42000000000000000000000000000000000000000000000000000000009060c40161210d565b6000818152600283016020526040812054801515806143fc57506143fc84846145f4565b610d36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f456e756d657261626c654d61703a206e6f6e6578697374656e74206b657900006044820152606401611195565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16610df1576144a28161485e565b6144ad83602061487d565b6040516020016144be9291906159e4565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527f08c379a000000000000000000000000000000000000000000000000000000000825261119591600401614e06565b6000610c7c82614ac0565b600080806145358585614aca565b600081815260029690960160205260409095205494959350505050565b60008281526002840160205260408120829055613b4b8484614ad6565b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516145999190615a65565b600060405180830381855af49150503d80600081146145d4576040519150601f19603f3d011682016040523d82523d6000602084013e6145d9565b606091505b50915091506145ea86838387614ae2565b9695505050505050565b6000610d368383614b82565b60008181526002830160205260408120819055610d368383614b9a565b6000610d368383670de0b6b3a76400006132fb565b82811161463f5780614641565b825b905060006146508286866132fb565b905082811161465f5780614661565b825b9050600081156146775761467482613c5e565b90505b6000614683600c613ba8565b905060005b818110156148545760008061469e600c84613bb3565b6040517f9b56d6c9000000000000000000000000000000000000000000000000000000008152306004820152919350915060009073ffffffffffffffffffffffffffffffffffffffff831690639b56d6c990602401602060405180830381865afa158015614710573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061473491906153ac565b90508060000361474657505050614842565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff841690639a78e72e90602401602060405180830381865afa1580156147b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147d791906153ac565b905060006147e68a848e6132fb565b905087158015906147f657508115155b156148325760006148088284866132fb565b905088811115614819575087614826565b614823818a615399565b98505b6148308682614219565b505b61483c8582613b53565b50505050505b8061484c816156b1565b915050614688565b5050505050505050565b6060610c7c73ffffffffffffffffffffffffffffffffffffffff831660145b6060600061488c836002615a81565b61489790600261569e565b67ffffffffffffffff8111156148af576148af6153c5565b6040519080825280601f01601f1916602001820160405280156148d9576020820181803683370190505b5090507f30000000000000000000000000000000000000000000000000000000000000008160008151811061491057614910615544565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f78000000000000000000000000000000000000000000000000000000000000008160018151811061497357614973615544565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006149af846002615a81565b6149ba90600161569e565b90505b6001811115614a57577f303132333435363738396162636465660000000000000000000000000000000085600f16601081106149fb576149fb615544565b1a60f81b828281518110614a1157614a11615544565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c93614a5081615a98565b90506149bd565b508315610d36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401611195565b6000610c7c825490565b6000610d368383614ba6565b6000610d368383614bd0565b60608315614b78578251600003614b715773ffffffffffffffffffffffffffffffffffffffff85163b614b71576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611195565b5081613b4b565b613b4b8383614c1f565b60008181526001830160205260408120541515610d36565b6000610d368383614c63565b6000826000018281548110614bbd57614bbd615544565b9060005260206000200154905092915050565b6000818152600183016020526040812054614c1757508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610c7c565b506000610c7c565b815115614c2f5781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111959190614e06565b60008181526001830160205260408120548015614d4c576000614c87600183615399565b8554909150600090614c9b90600190615399565b9050818114614d00576000866000018281548110614cbb57614cbb615544565b9060005260206000200154905080876000018481548110614cde57614cde615544565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080614d1157614d11615acd565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610c7c565b6000915050610c7c565b600060208284031215614d6857600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610d3657600080fd5b60005b83811015614db3578181015183820152602001614d9b565b50506000910152565b60008151808452614dd4816020860160208601614d98565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610d366020830184614dbc565b600060208284031215614e2b57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461339557600080fd5b60008060408385031215614e6757600080fd5b8235614e7281614e32565b946020939093013593505050565b60008060408385031215614e9357600080fd5b50508035926020909101359150565b600080600060608486031215614eb757600080fd5b8335614ec281614e32565b92506020840135614ed281614e32565b929592945050506040919091013590565b60008060408385031215614ef657600080fd5b823591506020830135614f0881614e32565b809150509250929050565b60008083601f840112614f2557600080fd5b50813567ffffffffffffffff811115614f3d57600080fd5b6020830191508360208260051b8501011115613bca57600080fd5b60008060208385031215614f6b57600080fd5b823567ffffffffffffffff811115614f8257600080fd5b614f8e85828601614f13565b90969095509350505050565b600060208284031215614fac57600080fd5b8135610d3681614e32565b60008083601f840112614fc957600080fd5b50813567ffffffffffffffff811115614fe157600080fd5b602083019150836020828501011115613bca57600080fd5b60008060006040848603121561500e57600080fd5b83359250602084013567ffffffffffffffff81111561502c57600080fd5b61503886828701614fb7565b9497909650939450505050565b8035801515811461505557600080fd5b919050565b6000806040838503121561506d57600080fd5b8235915061507d60208401615045565b90509250929050565b60008060008060008060a0878903121561509f57600080fd5b86356150aa81614e32565b955060208701356150ba81614e32565b945060408701359350606087013567ffffffffffffffff8111156150dd57600080fd5b6150e989828a01614fb7565b979a9699509497949695608090950135949350505050565b60008060006040848603121561511657600080fd5b83359250602084013567ffffffffffffffff81111561513457600080fd5b61503886828701614f13565b60008060006060848603121561515557600080fd5b83359250602084013561516781614e32565b9150604084013561517781614e32565b809150509250925092565b6000806040838503121561519557600080fd5b82356151a081614e32565b915061507d60208401615045565b600080600080600080600060e0888a0312156151c957600080fd5b87356151d481614e32565b965060208801356151e481614e32565b95506040880135945060608801359350608088013560ff8116811461520857600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561523857600080fd5b823561524381614e32565b91506020830135614f0881614e32565b6000806000806000806000806080898b03121561526f57600080fd5b883567ffffffffffffffff8082111561528757600080fd5b6152938c838d01614f13565b909a50985060208b01359150808211156152ac57600080fd5b6152b88c838d01614f13565b909850965060408b01359150808211156152d157600080fd5b6152dd8c838d01614f13565b909650945060608b01359150808211156152f657600080fd5b506153038b828c01614fb7565b999c989b5096995094979396929594505050565b600181811c9082168061532b57607f821691505b602082108103615364577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610c7c57610c7c61536a565b6000602082840312156153be57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561543b5761543b6153c5565b604052919050565b600067ffffffffffffffff8084111561545e5761545e6153c5565b8360051b602061546f8183016153f4565b8681529350908401908084018783111561548857600080fd5b855b8381101561552b578035858111156154a25760008081fd5b8701601f8082018b136154b55760008081fd5b8135878111156154c7576154c76153c5565b6154f6867fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084840116016153f4565b91508082528b8682850101111561550d5760008081fd5b8086840187840137600090820186015283525090820190820161548a565b50505050509392505050565b6000610d36368484615443565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600281106155d9577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b60208101610c7c82846155a2565b60006080820173ffffffffffffffffffffffffffffffffffffffff8088168452602060808186015282885180855260a087019150828a01945060005b81811015615645578551851683529483019491830191600101615627565b5050858103604087015287518082529082019350915080870160005b8381101561567d57815185529382019390820190600101615661565b5050505082810360608401526156938185614dbc565b979650505050505050565b80820180821115610c7c57610c7c61536a565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036156e2576156e261536a565b5060010190565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b602081526000613b4b6020830184866156e9565b600073ffffffffffffffffffffffffffffffffffffffff808916835280881660208401525085604083015284606083015260a0608083015261578c60a0830184866156e9565b98975050505050505050565b6000604082016157a883876155a2565b60206040818501528185835260608501905060608660051b86010192508660005b87811015615874577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa087860301835281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18a360301811261582a57600080fd5b8901848101903567ffffffffffffffff81111561584657600080fd5b80360382131561585557600080fd5b6158608782846156e9565b9650505091830191908301906001016157c9565b509298975050505050505050565b80356002811061505557600080fd5b6000602082840312156158a357600080fd5b610d3682615882565b600080604083850312156158bf57600080fd5b6158c883615882565b9150602083013567ffffffffffffffff8111156158e457600080fd5b8301601f810185136158f557600080fd5b61590485823560208401615443565b9150509250929050565b600080835481600182811c91508083168061592a57607f831692505b60208084108203615962577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b81801561597657600181146159a9576159d6565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00861689528415158502890196506159d6565b60008a81526020902060005b868110156159ce5781548b8201529085019083016159b5565b505084890196505b509498975050505050505050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351615a1c816017850160208801614d98565b7f206973206d697373696e6720726f6c65200000000000000000000000000000006017918401918201528351615a59816028840160208801614d98565b01602801949350505050565b60008251615a77818460208701614d98565b9190910192915050565b8082028115828204841417610c7c57610c7c61536a565b600081615aa757615aa761536a565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212200bc2f86f47ec958b85c0f4b2848debea506a200bb81e282c9cdc4f18bd187a4364736f6c6343000815003300000000000000000000000084f67f75daf6d57aef500e0c85c77b7b3bbc92a9000000000000000000000000397502f15e11c524f23c0c003f5e8004c1c5c71d0000000000000000000000004c406c068106375724275cbff028770c544a1333000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca0000000000000000000000006649f12b5ef495a3861b21e3206b1abfa33a6531
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106104f95760003560e01c806373688e56116102a1578063b93855101161016b578063d505accf116100e3578063dd62ed3e11610097578063f04f27071161007c578063f04f270714610b8c578063f0f4426014610b9f578063fc7b9c1814610bb257600080fd5b8063dd62ed3e14610b4e578063ef8b30f714610b7957600080fd5b8063d8aed145116100c8578063d8aed14514610af2578063d905777e14610b05578063daecfa6c14610b3b57600080fd5b8063d505accf14610acc578063d547741f14610adf57600080fd5b8063c7e106d81161013a578063c9dd1f3c1161011f578063c9dd1f3c14610a9d578063ce96cb7714610ab0578063d03153aa14610ac357600080fd5b8063c7e106d814610a77578063c923299a14610a8a57600080fd5b8063b938551014610a2a578063ba08765214610a51578063c63d75b61461075a578063c6e6f59214610a6457600080fd5b80639ceffcde11610219578063b0bfa1b3116101cd578063b460af94116101b2578063b460af94146109cb578063b5af090f146109de578063b5ba383314610a1757600080fd5b8063b0bfa1b314610991578063b3d7f6b9146109b857600080fd5b8063a217fddf116101fe578063a217fddf14610963578063a34130ae1461096b578063a9059cbb1461097e57600080fd5b80639ceffcde1461093d5780639ef009061461095057600080fd5b80638d60cded1161027057806394bf804d1161025557806394bf804d1461090f57806395d89b41146109225780639c82f2a41461092a57600080fd5b80638d60cded146108b657806391d14854146108c957600080fd5b806373688e561461087d578063773a6c4f146108855780637ecebe001461088d57806387788782146108ad57600080fd5b806336568abe116103e2578063549dd8c31161035a578063679dffb41161030e5780636e553f65116102f35780636e553f651461083757806370897b231461084a57806370a082311461085d57600080fd5b8063679dffb41461081c5780636860e9d21461082457600080fd5b806360d54d411161033f57806360d54d41146107d757806361d027b3146107ea5780636457f7551461080f57600080fd5b8063549dd8c3146107bb5780635892457d146107ce57600080fd5b80633fc8cef3116103b1578063441a3e7011610396578063441a3e701461078d5780634ac8eb5f146107a05780634cdad506146107a857600080fd5b80633fc8cef31461073f578063402d267d1461075a57600080fd5b806336568abe146106df57806338d52e0f146106f2578063392179c6146107195780633c0393821461072c57600080fd5b806318160ddd116104755780632b3297f911610444578063313ce56711610429578063313ce567146106775780633644e515146106b0578063364bc15a146106b857600080fd5b80632b3297f9146106445780632f2ff15d1461066457600080fd5b806318160ddd146105f257806323b872dd146105fb578063248a9ca31461060e5780632a62a4901461063157600080fd5b8063095ea7b3116104cc5780630ecbcdab116104b15780630ecbcdab1461058a578063117da1ee1461059f578063158274a5146105b257600080fd5b8063095ea7b3146105645780630a28a4771461057757600080fd5b806301e1d114146104fe57806301ffc9a71461051957806306fdde031461053c57806307a2d13a14610551575b600080fd5b610506610bba565b6040519081526020015b60405180910390f35b61052c610527366004614d56565b610be9565b6040519015158152602001610510565b610544610c82565b6040516105109190614e06565b61050661055f366004614e19565b610d10565b61052c610572366004614e54565b610d3d565b610506610585366004614e19565b610db6565b61059d610598366004614e80565b610dd6565b005b61059d6105ad366004614e19565b610df5565b6105cd73ba12222222228d8ba445958a75a0704d566bf2c881565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610510565b61050660025481565b61052c610609366004614ea2565b610e7d565b61050661061c366004614e19565b60009081526006602052604090206001015490565b61050661063f366004614e19565b610fc1565b600b546105cd9073ffffffffffffffffffffffffffffffffffffffff1681565b61059d610672366004614ee3565b611074565b61069e7f000000000000000000000000000000000000000000000000000000000000000681565b60405160ff9091168152602001610510565b61050661109e565b6105067ffc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab81565b61059d6106ed366004614ee3565b6110f4565b6105cd7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b61059d610727366004614f58565b6111a8565b61059d61073a366004614e19565b611289565b6105cd73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b610506610768366004614f9a565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90565b61059d61079b366004614e80565b61166d565b610506611688565b6105066107b6366004614e19565b611766565b61059d6107c9366004614ff9565b611771565b61050660095481565b61059d6107e5366004614f9a565b61185c565b6007546105cd90610100900473ffffffffffffffffffffffffffffffffffffffff1681565b60075461052c9060ff1681565b6105066119f5565b61059d610832366004614e19565b611a0f565b610506610845366004614ee3565b611a20565b61059d610858366004614e19565b611b3c565b61050661086b366004614f9a565b60036020526000908152604090205481565b610506611bbd565b610506611c6f565b61050661089b366004614f9a565b60056020526000908152604090205481565b61050660085481565b61052c6108c4366004614e19565b611d46565b61052c6108d7366004614ee3565b600091825260066020908152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b61050661091d366004614ee3565b611d53565b610544611dfc565b61059d610938366004614f9a565b611e09565b61059d61094b36600461505a565b611ed3565b61059d61095e366004615086565b612066565b610506600081565b61059d610979366004615101565b612220565b61052c61098c366004614e54565b61244c565b6105cd7f0000000000000000000000004c406c068106375724275cbff028770c544a133381565b6105066109c6366004614e19565b6124d1565b6105066109d9366004615140565b6124f0565b61052c6109ec366004614f9a565b73ffffffffffffffffffffffffffffffffffffffff166000908152600f602052604090205460ff1690565b61059d610a25366004614e19565b61265d565b6105cd7f000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca81565b610506610a5f366004615140565b612700565b610506610a72366004614e19565b6128d7565b61059d610a85366004615182565b6128f7565b61059d610a98366004614e19565b6129db565b610506610aab366004614e19565b612a5c565b610506610abe366004614f9a565b612ad2565b610506600a5481565b61059d610ada3660046151ae565b612b01565b61059d610aed366004614ee3565b612e20565b61059d610b00366004614e80565b612e45565b610506610b13366004614f9a565b73ffffffffffffffffffffffffffffffffffffffff1660009081526003602052604090205490565b61059d610b49366004614e80565b612e60565b610506610b5c366004615225565b600460209081526000928352604080842090915290825290205481565b610506610b87366004614e19565b612e7b565b61059d610b9a366004615253565b612e86565b61059d610bad366004614f9a565b612f6f565b610506613040565b6000610be4610bc7611bbd565b610bcf611688565b610bd7611c6f565b610bdf613040565b613119565b905090565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b000000000000000000000000000000000000000000000000000000001480610c7c57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60008054610c8f90615317565b80601f0160208091040260200160405190810160405280929190818152602001828054610cbb90615317565b8015610d085780601f10610cdd57610100808354040283529160200191610d08565b820191906000526020600020905b815481529060010190602001808311610ceb57829003601f168201915b505050505081565b6002546000908015610d3457610d2f610d27610bba565b8490836132bf565b610d36565b825b9392505050565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610da59086815260200190565b60405180910390a350600192915050565b6002546000908015610d3457610d2f81610dce610bba565b8591906132fb565b610dde61333f565b610de782613353565b610df18282613398565b5050565b610dfd6133ed565b670de0b6b3a7640000811115610e3f576040517fc31c0b6e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a81905560405181815233907fb3a17cf8bbe3a4348266beede3a365af15dd59203021a6c1121d00ca2d5019f7906020015b60405180910390a250565b73ffffffffffffffffffffffffffffffffffffffff831660009081526004602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610f1157610edf8382615399565b73ffffffffffffffffffffffffffffffffffffffff861660009081526004602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff851660009081526003602052604081208054859290610f46908490615399565b909155505073ffffffffffffffffffffffffffffffffffffffff808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90610fae9087815260200190565b60405180910390a3506001949350505050565b6000610fcc82611d46565b610fd857506000919050565b610fe3600c83613455565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639b56d6c9906024015b602060405180830381865afa158015611050573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c7c91906153ac565b60008281526006602052604090206001015461108f81613461565b611099838361346b565b505050565b60007f000000000000000000000000000000000000000000000000000000000000000146146110cf57610be461355f565b507f092c4bf5ac69cdfd87adb7ac39ff8cb298489d7f4c5dd719b8d8dd5c388fc3cb90565b73ffffffffffffffffffffffffffffffffffffffff8116331461119e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b610df182826135f9565b6111b06136b4565b6111c26111bd8284615537565b61371c565b6111ca6137a1565b60006111d4611bbd565b905060006111ec6009546111e6610bba565b906138a1565b905080821015611232576040517fcd62da430000000000000000000000000000000000000000000000000000000081526004810183905260248101829052604401611195565b7f83387a3342ff1ebc5e437dc9ae0f98274afda12a11cf547eebec05a3e0b8f8a761125b611688565b611263613040565b60408051928352602083019190915281018490526060015b60405180910390a150505050565b6112916136b4565b600061129b611688565b905060006112a7613040565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290915060009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004c406c068106375724275cbff028770c544a1333169063ba0876529082906370a0823190602401602060405180830381865afa15801561133f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061136391906153ac565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526004810191909152306024820181905260448201526064016020604051808303816000875af11580156113c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ea91906153ac565b9050808211156115ba576040805160018082528183019092526000916020808301908036833701905050905073c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28160008151811061143e5761143e615544565b73ffffffffffffffffffffffffffffffffffffffff92909216602092830291909101909101526040805160018082528183019092526000918160200160208202803683370190505090506114928385615399565b816000815181106114a5576114a5615544565b6020026020010181815250506114e1600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b73ba12222222228d8ba445958a75a0704d566bf2c873ffffffffffffffffffffffffffffffffffffffff16635c38449e308484600160405160200161152691906155dd565b6040516020818303038152906040526040518563ffffffff1660e01b815260040161155494939291906155eb565b600060405180830381600087803b15801561156e57600080fd5b505af1158015611582573d6000803e3d6000fd5b505050506115b3600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b50506115e3565b6115c26138b6565b60006115cc613a40565b905080156115e1576115df816000613a93565b505b505b836115ec611bbd565b1015611624576040517f80fece5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518281526020810184905290810184905233907f8f8ca188bc27143e106193f8f078b05437841134a613ade83e2642758c0e09009060600160405180910390a250505050565b61167561333f565b61167e82613353565b610df18282613b53565b600080611695600c613ba8565b905060005b818110156117615760006116af600c83613bb3565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015290925073ffffffffffffffffffffffffffffffffffffffff83169150639b56d6c990602401602060405180830381865afa15801561171d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061174191906153ac565b61174b908561569e565b9350508080611759906156b1565b91505061169a565b505090565b6000610c7c82610d10565b61177961333f565b61178283613353565b6118238363e190febc60e01b84846040516024016117a1929190615732565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613bd1565b6040518381527fed6771ea2f01816faa7ccf034b76201581d0b4374e86f83b06a8b4191c6b9f89906020015b60405180910390a1505050565b6118646133ed565b60008173ffffffffffffffffffffffffffffffffffffffff1663af640d0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118d591906153ac565b90506118e081611d46565b1561191a576040517f254de06900000000000000000000000000000000000000000000000000000000815260048101829052602401611195565b611926600c8284613bfc565b506040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f8757b15b0000000000000000000000000000000000000000000000000000000017905261199f9073ffffffffffffffffffffffffffffffffffffffff841690613c1f565b506040805182815273ffffffffffffffffffffffffffffffffffffffff8416602082015233917f7ae8ce35a1794068c85a72f63d16209787d9e4f3e4b3095cf7f0e1de1e5e074191015b60405180910390a25050565b6000610be4611a02611c6f565b611a0a613040565b613c44565b611a176136b4565b610df181613c5e565b6000611a2b83612e7b565b905080600003611a97576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f5348415245530000000000000000000000000000000000000000006044820152606401611195565b611ad973ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816333086613e18565b611ae38282613ede565b604080518481526020810183905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d791015b60405180910390a3610c7c565b611b446133ed565b670de0b6b3a7640000811115611b86576040517fc9034e1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600881905560405181815233907f48d9d467a6699d3c9f09688328d42ebb56b0d1598687d06bebbe2e7f4264156290602001610e72565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff16906370a08231906024015b602060405180830381865afa158015611c4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be491906153ac565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f0000000000000000000000004c406c068106375724275cbff028770c544a133373ffffffffffffffffffffffffffffffffffffffff16906307a2d13a9082906370a0823190602401602060405180830381865afa158015611d04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d2891906153ac565b6040518263ffffffff1660e01b8152600401611c2e91815260200190565b6000610c7c600c83613f57565b6000611d5e836124d1565b9050611da273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816333084613e18565b611dac8284613ede565b604080518281526020810185905273ffffffffffffffffffffffffffffffffffffffff84169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d79101611b2f565b60018054610c8f90615317565b611e116133ed565b73ffffffffffffffffffffffffffffffffffffffff8116611e5e576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560405190815233907ffb7a5f1d35a7022d9d6343bfc9a25035829d0ea72da06978793c945b1d94a17f90602001610e72565b611edb6133ed565b611ee482613353565b80158015611f8c57506000611efa600c84613455565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639b56d6c990602401602060405180830381865afa158015611f66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f8a91906153ac565b115b15611fc6576040517f254de06900000000000000000000000000000000000000000000000000000000815260048101839052602401611195565b6040805160048152602481019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9265a7d500000000000000000000000000000000000000000000000000000000179052612028908390613bd1565b612033600c83613f63565b5060405182815233907fce27e7409a7e5bc40ecb15d4e938f30ecc5e1e44b03b8f9517eb0a3bf906eccc906020016119e9565b61206e61333f565b73ffffffffffffffffffffffffffffffffffffffff85166000908152600f602052604090205460ff166120e5576040517fcc2342f100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86166004820152602401611195565b60006121a9631f2913d260e01b88888886898960405160240161210d96959493929190615746565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152600b5473ffffffffffffffffffffffffffffffffffffffff1690613c1f565b90507fd5a1cd88ddd329cc1ddf861a21708efa9ef3136b88e1420c21f91ef6f95ad0248786838060200190518101906121e291906153ac565b6040805173ffffffffffffffffffffffffffffffffffffffff909416845260208401929092529082015260600160405180910390a150505050505050565b6122286136b4565b82600003612262576040517f667000d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160018082528183019092526000916020808301908036833701905050905073c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2816000815181106122ac576122ac615544565b73ffffffffffffffffffffffffffffffffffffffff9290921660209283029190910190910152604080516001808252818301909252600091816020016020820280368337019050509050848160008151811061230a5761230a615544565b602002602001018181525050612346600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b73ba12222222228d8ba445958a75a0704d566bf2c873ffffffffffffffffffffffffffffffffffffffff16635c38449e3084846000898960405160200161238f93929190615798565b6040516020818303038152906040526040518563ffffffff1660e01b81526004016123bd94939291906155eb565b600060405180830381600087803b1580156123d757600080fd5b505af11580156123eb573d6000803e3d6000fd5b5050505061241c600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b6040517f9beffa21c10379adc18939ddf9613b794f42e714f3ef783faae5d011d0389e5590600090a15050505050565b3360009081526003602052604081208054839190839061246d908490615399565b909155505073ffffffffffffffffffffffffffffffffffffffff8316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90610da59086815260200190565b6002546000908015610d3457610d2f6124e8610bba565b8490836132fb565b60006124fb84610db6565b90503373ffffffffffffffffffffffffffffffffffffffff8316146125b05773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146125ae5761257c8282615399565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b6125ba8482613f6f565b6125c482826140cc565b604080518581526020810183905273ffffffffffffffffffffffffffffffffffffffff808516929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a4610d3673ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816848661415a565b6126656136b4565b6000612672611a02611c6f565b9050806000036126ae576040517f6733d0e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006126b982613c5e565b905060006126c78285613a93565b60408051848152602081018390529192507fa0b83a013fc7994bd0e79a69e2adb2ddda2cb17acf657a73120900b26edcd206910161127b565b60003373ffffffffffffffffffffffffffffffffffffffff8316146127b55773ffffffffffffffffffffffffffffffffffffffff821660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146127b3576127818582615399565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020555b505b6127be84611766565b90508060000361282a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f5a45524f5f4153534554530000000000000000000000000000000000000000006044820152606401611195565b6128348185613f6f565b61283e82856140cc565b604080518281526020810186905273ffffffffffffffffffffffffffffffffffffffff808516929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a4610d3673ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816848361415a565b6002546000908015610d3457610d2f816128ef610bba565b8591906132bf565b6128ff6133ed565b73ffffffffffffffffffffffffffffffffffffffff821661294c576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000818152600f602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168515159081179091558251938452908301527fef81a9943b96c8df4ef243401c9bf5159146166211356898b52d382086168d9291015b60405180910390a15050565b6129e36133ed565b670de0b6b3a7640000811115612a25576040517f7bfb537900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600981905560405181815233907fb414942c66de9d94d6f12b7818ecb6c326ee7f58980297cfba9a54b979e01d3390602001610e72565b6000612a6782611d46565b612a7357506000919050565b612a7e600c83613455565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9190911690639a78e72e90602401611033565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260036020526040812054610c7c90610d10565b42841015612b6b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401611195565b60006001612b7761109e565b73ffffffffffffffffffffffffffffffffffffffff8a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015612cc9573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590612d4457508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b612daa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152606401611195565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b600082815260066020526040902060010154612e3b81613461565b61109983836135f9565b612e4d61333f565b612e5682613353565b610df18282614219565b612e6861333f565b612e7182613353565b610df1828261428b565b6000610c7c826128d7565b612e8e6142e0565b600086866000818110612ea357612ea3615544565b90506020020135905060008383810190612ebd9190615891565b90506001816001811115612ed357612ed3615573565b03612eee57612ee06138b6565b612ee98261431c565b612f0a565b6000612efc848601866158ac565b915050612f088161371c565b505b612f6373ba12222222228d8ba445958a75a0704d566bf2c887876000818110612f3557612f35615544565b9050602002013584612f47919061569e565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2919061415a565b50505050505050505050565b612f776133ed565b73ffffffffffffffffffffffffffffffffffffffff8116612fc4576040517f91f7acdb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600780547fffffffffffffffffffffff0000000000000000000000000000000000000000ff1661010073ffffffffffffffffffffffffffffffffffffffff84169081029190911790915560405190815233907f4ab5be82436d353e61ca18726e984e561f5c1cc7c6d38b29d2553c790434705a90602001610e72565b60008061304d600c613ba8565b905060005b81811015611761576000613067600c83613bb3565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015290925073ffffffffffffffffffffffffffffffffffffffff83169150639a78e72e90602401602060405180830381865afa1580156130d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130f991906153ac565b613103908561569e565b9350508080613111906156b1565b915050613052565b6000613125848661569e565b905060006131338484613c44565b9050801561320457600a546040517fd8bddbd8000000000000000000000000000000000000000000000000000000008152600481018390526131f3919073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca169063d8bddbd8906024015b602060405180830381865afa1580156131cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e691906153ac565b6131fd908361569e565b91506132b6565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca1663d8bddbd861324a8686615399565b6040518263ffffffff1660e01b815260040161326891815260200190565b602060405180830381865afa158015613285573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132a991906153ac565b6132b39083615399565b91505b50949350505050565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04841183021582026132f457600080fd5b5091020490565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048411830215820261333057600080fd5b50910281810615159190040190565b60075460ff16613351576133516136b4565b565b61335c81611d46565b613395576040517f10028b5400000000000000000000000000000000000000000000000000000000815260048101829052602401611195565b50565b6133b78263c5ebeaec60e01b836040516024016117a191815260200190565b60408051838152602081018390527f14a13f958d7fe38fb8529e8d43353b6d9120f52444e486a4077f68a3fc77dcbf91016129cf565b3360009081527f54cdd369e4e8a8515e52ca72ec816c2101831ad1f18bf44102ed171459c9b4f8602052604090205460ff16613351576040517f06d919f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610d3683836143d8565b6133958133614462565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16610df157600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff85168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556135013390565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051613591919061590e565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff1615610df157600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b3360009081527f102c8bff359ba7f792f3597c956360de34af36b3eee541f0c28549f821bb5369602052604090205460ff16613351576040517f04e1fa9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8151811015610df15781818151811061373a5761373a615544565b6020026020010151516000031561378f5761378d82828151811061376057613760615544565b60200260200101513073ffffffffffffffffffffffffffffffffffffffff16613c1f90919063ffffffff16565b505b80613799816156b1565b91505061371f565b60006137ab613a40565b90508015613395576040517f6e553f65000000000000000000000000000000000000000000000000000000008152600481018290523060248201527f0000000000000000000000004c406c068106375724275cbff028770c544a133373ffffffffffffffffffffffffffffffffffffffff1690636e553f65906044016020604051808303816000875af1158015613846573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061386a91906153ac565b506040518181527fac9f7997c30b6a3cc9c74953898b5de154359339c7ec0d6d70ceb98e55db1a4b9060200160405180910390a150565b6000610d368383670de0b6b3a76400006132bf565b60006138c2600c613ba8565b905060005b81811015610df1576000806138dd600c84613bb3565b6040517f9a78e72e000000000000000000000000000000000000000000000000000000008152306004820152919350915060009073ffffffffffffffffffffffffffffffffffffffff831690639a78e72e90602401602060405180830381865afa15801561394f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061397391906153ac565b6040517f9b56d6c900000000000000000000000000000000000000000000000000000000815230600482015290915060009073ffffffffffffffffffffffffffffffffffffffff841690639b56d6c990602401602060405180830381865afa1580156139e3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a0791906153ac565b90508115613a1957613a198483614219565b8015613a2957613a298482613b53565b505050508080613a38906156b1565b9150506138c7565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2906370a0823190602401611c2e565b60405173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2602482015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816604482015260648101839052608481018290526101f460a48201526000908190613b35907f0db634ba000000000000000000000000000000000000000000000000000000009060c40161210d565b905080806020019051810190613b4b91906153ac565b949350505050565b613b7282632e1a7d4d60e01b836040516024016117a191815260200190565b60408051838152602081018390527f0c875c8d391179c5cf7ad8303d268efd50b8beb78b671f85cd54bfb91eb8ef4091016129cf565b6000610c7c8261451c565b6000808080613bc28686614527565b909450925050505b9250929050565b61109981613be0600c85613455565b73ffffffffffffffffffffffffffffffffffffffff1690613c1f565b6000613b4b848473ffffffffffffffffffffffffffffffffffffffff8516614552565b6060610d368383604051806060016040528060278152602001615afd6027913961456f565b6000818311613c54576000610d36565b610d368284615399565b6040517fc6e6f59200000000000000000000000000000000000000000000000000000000815260048101829052600090819073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004c406c068106375724275cbff028770c544a1333169063c6e6f59290602401602060405180830381865afa158015613cee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d1291906153ac565b6040517fba087652000000000000000000000000000000000000000000000000000000008152600481018290523060248201819052604482015290915060009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004c406c068106375724275cbff028770c544a1333169063ba087652906064016020604051808303816000875af1158015613db2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dd691906153ac565b90507ff7e72e2f53a982a67fc999c00edfd87a94bdf99e5031c74851276cfd65b0bd2981604051613e0991815260200190565b60405180910390a19392505050565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080613ed7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401611195565b5050505050565b8060026000828254613ef0919061569e565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b6000610d3683836145f4565b6000610d368383614600565b6000613f79611bbd565b9050828110613f8757505050565b6000613f91611688565b90506000613f9d613040565b90506000613fa9611c6f565b90506000613fb985858486613119565b90506000613fc78385613c44565b90506000888311613fd9576000613ff0565b600954613ff090613fea8b86615399565b9061461d565b9050600087613fff838c61569e565b6140099190615399565b905082156140c057600061401c84613c5e565b90506000614081600a547f000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca73ffffffffffffffffffffffffffffffffffffffff1663d8bddbd8856040518263ffffffff1660e01b81526004016131b291815260200190565b9050600061408f8383613a93565b90508c61409c828d61569e565b106140b05750505050505050505050505050565b6140ba8185615399565b93505050505b612f6386888784614632565b73ffffffffffffffffffffffffffffffffffffffff821660009081526003602052604081208054839290614101908490615399565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001613f4b565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080614213576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401611195565b50505050565b6000614223613a40565b90508082116142325781614234565b805b91506142558363371fd8e660e01b846040516024016117a191815260200190565b60408051848152602081018490527f81472a96709c8315c82af40d41ef624a642ad53864b097e53af675593bb4e035910161184f565b6142aa82633540302360e01b836040516024016117a191815260200190565b60408051838152602081018390527f3b81a6e0eae880a578a3b45b9345aa6c424b285b830d9a4037b63f50b8380f4391016129cf565b60075460ff16613351576040517fe17c49b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816602482015273c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26044820152606481018290527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60848201526101f460a4820152610df1907f4ad44e42000000000000000000000000000000000000000000000000000000009060c40161210d565b6000818152600283016020526040812054801515806143fc57506143fc84846145f4565b610d36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f456e756d657261626c654d61703a206e6f6e6578697374656e74206b657900006044820152606401611195565b600082815260066020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16610df1576144a28161485e565b6144ad83602061487d565b6040516020016144be9291906159e4565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527f08c379a000000000000000000000000000000000000000000000000000000000825261119591600401614e06565b6000610c7c82614ac0565b600080806145358585614aca565b600081815260029690960160205260409095205494959350505050565b60008281526002840160205260408120829055613b4b8484614ad6565b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516145999190615a65565b600060405180830381855af49150503d80600081146145d4576040519150601f19603f3d011682016040523d82523d6000602084013e6145d9565b606091505b50915091506145ea86838387614ae2565b9695505050505050565b6000610d368383614b82565b60008181526002830160205260408120819055610d368383614b9a565b6000610d368383670de0b6b3a76400006132fb565b82811161463f5780614641565b825b905060006146508286866132fb565b905082811161465f5780614661565b825b9050600081156146775761467482613c5e565b90505b6000614683600c613ba8565b905060005b818110156148545760008061469e600c84613bb3565b6040517f9b56d6c9000000000000000000000000000000000000000000000000000000008152306004820152919350915060009073ffffffffffffffffffffffffffffffffffffffff831690639b56d6c990602401602060405180830381865afa158015614710573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061473491906153ac565b90508060000361474657505050614842565b6040517f9a78e72e00000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff841690639a78e72e90602401602060405180830381865afa1580156147b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147d791906153ac565b905060006147e68a848e6132fb565b905087158015906147f657508115155b156148325760006148088284866132fb565b905088811115614819575087614826565b614823818a615399565b98505b6148308682614219565b505b61483c8582613b53565b50505050505b8061484c816156b1565b915050614688565b5050505050505050565b6060610c7c73ffffffffffffffffffffffffffffffffffffffff831660145b6060600061488c836002615a81565b61489790600261569e565b67ffffffffffffffff8111156148af576148af6153c5565b6040519080825280601f01601f1916602001820160405280156148d9576020820181803683370190505b5090507f30000000000000000000000000000000000000000000000000000000000000008160008151811061491057614910615544565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f78000000000000000000000000000000000000000000000000000000000000008160018151811061497357614973615544565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006149af846002615a81565b6149ba90600161569e565b90505b6001811115614a57577f303132333435363738396162636465660000000000000000000000000000000085600f16601081106149fb576149fb615544565b1a60f81b828281518110614a1157614a11615544565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c93614a5081615a98565b90506149bd565b508315610d36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401611195565b6000610c7c825490565b6000610d368383614ba6565b6000610d368383614bd0565b60608315614b78578251600003614b715773ffffffffffffffffffffffffffffffffffffffff85163b614b71576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611195565b5081613b4b565b613b4b8383614c1f565b60008181526001830160205260408120541515610d36565b6000610d368383614c63565b6000826000018281548110614bbd57614bbd615544565b9060005260206000200154905092915050565b6000818152600183016020526040812054614c1757508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610c7c565b506000610c7c565b815115614c2f5781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111959190614e06565b60008181526001830160205260408120548015614d4c576000614c87600183615399565b8554909150600090614c9b90600190615399565b9050818114614d00576000866000018281548110614cbb57614cbb615544565b9060005260206000200154905080876000018481548110614cde57614cde615544565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080614d1157614d11615acd565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610c7c565b6000915050610c7c565b600060208284031215614d6857600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610d3657600080fd5b60005b83811015614db3578181015183820152602001614d9b565b50506000910152565b60008151808452614dd4816020860160208601614d98565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610d366020830184614dbc565b600060208284031215614e2b57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461339557600080fd5b60008060408385031215614e6757600080fd5b8235614e7281614e32565b946020939093013593505050565b60008060408385031215614e9357600080fd5b50508035926020909101359150565b600080600060608486031215614eb757600080fd5b8335614ec281614e32565b92506020840135614ed281614e32565b929592945050506040919091013590565b60008060408385031215614ef657600080fd5b823591506020830135614f0881614e32565b809150509250929050565b60008083601f840112614f2557600080fd5b50813567ffffffffffffffff811115614f3d57600080fd5b6020830191508360208260051b8501011115613bca57600080fd5b60008060208385031215614f6b57600080fd5b823567ffffffffffffffff811115614f8257600080fd5b614f8e85828601614f13565b90969095509350505050565b600060208284031215614fac57600080fd5b8135610d3681614e32565b60008083601f840112614fc957600080fd5b50813567ffffffffffffffff811115614fe157600080fd5b602083019150836020828501011115613bca57600080fd5b60008060006040848603121561500e57600080fd5b83359250602084013567ffffffffffffffff81111561502c57600080fd5b61503886828701614fb7565b9497909650939450505050565b8035801515811461505557600080fd5b919050565b6000806040838503121561506d57600080fd5b8235915061507d60208401615045565b90509250929050565b60008060008060008060a0878903121561509f57600080fd5b86356150aa81614e32565b955060208701356150ba81614e32565b945060408701359350606087013567ffffffffffffffff8111156150dd57600080fd5b6150e989828a01614fb7565b979a9699509497949695608090950135949350505050565b60008060006040848603121561511657600080fd5b83359250602084013567ffffffffffffffff81111561513457600080fd5b61503886828701614f13565b60008060006060848603121561515557600080fd5b83359250602084013561516781614e32565b9150604084013561517781614e32565b809150509250925092565b6000806040838503121561519557600080fd5b82356151a081614e32565b915061507d60208401615045565b600080600080600080600060e0888a0312156151c957600080fd5b87356151d481614e32565b965060208801356151e481614e32565b95506040880135945060608801359350608088013560ff8116811461520857600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561523857600080fd5b823561524381614e32565b91506020830135614f0881614e32565b6000806000806000806000806080898b03121561526f57600080fd5b883567ffffffffffffffff8082111561528757600080fd5b6152938c838d01614f13565b909a50985060208b01359150808211156152ac57600080fd5b6152b88c838d01614f13565b909850965060408b01359150808211156152d157600080fd5b6152dd8c838d01614f13565b909650945060608b01359150808211156152f657600080fd5b506153038b828c01614fb7565b999c989b5096995094979396929594505050565b600181811c9082168061532b57607f821691505b602082108103615364577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610c7c57610c7c61536a565b6000602082840312156153be57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561543b5761543b6153c5565b604052919050565b600067ffffffffffffffff8084111561545e5761545e6153c5565b8360051b602061546f8183016153f4565b8681529350908401908084018783111561548857600080fd5b855b8381101561552b578035858111156154a25760008081fd5b8701601f8082018b136154b55760008081fd5b8135878111156154c7576154c76153c5565b6154f6867fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084840116016153f4565b91508082528b8682850101111561550d5760008081fd5b8086840187840137600090820186015283525090820190820161548a565b50505050509392505050565b6000610d36368484615443565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600281106155d9577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b60208101610c7c82846155a2565b60006080820173ffffffffffffffffffffffffffffffffffffffff8088168452602060808186015282885180855260a087019150828a01945060005b81811015615645578551851683529483019491830191600101615627565b5050858103604087015287518082529082019350915080870160005b8381101561567d57815185529382019390820190600101615661565b5050505082810360608401526156938185614dbc565b979650505050505050565b80820180821115610c7c57610c7c61536a565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036156e2576156e261536a565b5060010190565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b602081526000613b4b6020830184866156e9565b600073ffffffffffffffffffffffffffffffffffffffff808916835280881660208401525085604083015284606083015260a0608083015261578c60a0830184866156e9565b98975050505050505050565b6000604082016157a883876155a2565b60206040818501528185835260608501905060608660051b86010192508660005b87811015615874577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa087860301835281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18a360301811261582a57600080fd5b8901848101903567ffffffffffffffff81111561584657600080fd5b80360382131561585557600080fd5b6158608782846156e9565b9650505091830191908301906001016157c9565b509298975050505050505050565b80356002811061505557600080fd5b6000602082840312156158a357600080fd5b610d3682615882565b600080604083850312156158bf57600080fd5b6158c883615882565b9150602083013567ffffffffffffffff8111156158e457600080fd5b8301601f810185136158f557600080fd5b61590485823560208401615443565b9150509250929050565b600080835481600182811c91508083168061592a57607f831692505b60208084108203615962577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b81801561597657600181146159a9576159d6565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00861689528415158502890196506159d6565b60008a81526020902060005b868110156159ce5781548b8201529085019083016159b5565b505084890196505b509498975050505050505050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351615a1c816017850160208801614d98565b7f206973206d697373696e6720726f6c65200000000000000000000000000000006017918401918201528351615a59816028840160208801614d98565b01602801949350505050565b60008251615a77818460208701614d98565b9190910192915050565b8082028115828204841417610c7c57610c7c61536a565b600081615aa757615aa761536a565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212200bc2f86f47ec958b85c0f4b2848debea506a200bb81e282c9cdc4f18bd187a4364736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000084f67f75daf6d57aef500e0c85c77b7b3bbc92a9000000000000000000000000397502f15e11c524f23c0c003f5e8004c1c5c71d0000000000000000000000004c406c068106375724275cbff028770c544a1333000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca0000000000000000000000006649f12b5ef495a3861b21e3206b1abfa33a6531
-----Decoded View---------------
Arg [0] : _admin (address): 0x84f67f75DAf6D57Aef500E0c85C77B7b3bBc92A9
Arg [1] : _keeper (address): 0x397502F15E11C524F23C0c003f5E8004C1c5c71D
Arg [2] : _scWETH (address): 0x4c406C068106375724275Cbff028770C544a1333
Arg [3] : _priceConverter (address): 0xD76B0Ff4A487CaFE4E19ed15B73f12f6A92095Ca
Arg [4] : _swapper (address): 0x6649f12b5ef495a3861b21E3206B1AbfA33A6531
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 00000000000000000000000084f67f75daf6d57aef500e0c85c77b7b3bbc92a9
Arg [1] : 000000000000000000000000397502f15e11c524f23c0c003f5e8004c1c5c71d
Arg [2] : 0000000000000000000000004c406c068106375724275cbff028770c544a1333
Arg [3] : 000000000000000000000000d76b0ff4a487cafe4e19ed15b73f12f6a92095ca
Arg [4] : 0000000000000000000000006649f12b5ef495a3861b21e3206b1abfa33a6531
Loading...
Loading
Loading...
Loading
OVERVIEW
Sandclock USDC YieldLoading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.