Source Code
Overview
S Balance
S Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Swapper
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import {AmmAdapterIdLib} from "../adapters/libs/AmmAdapterIdLib.sol";
import {IMetaVaultAmmAdapter} from "../interfaces/IMetaVaultAmmAdapter.sol";
import {Controllable} from "./base/Controllable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {IAmmAdapter} from "../interfaces/IAmmAdapter.sol";
import {IControllable} from "../interfaces/IControllable.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IPlatform} from "../interfaces/IPlatform.sol";
import {ISwapper} from "../interfaces/ISwapper.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/// @notice On-chain price quoter and swapper. It works by predefined routes using AMM adapters.
/// @dev Inspired by TetuLiquidator
/// Changelog:
/// 1.3.0: - exclude cycling routes - #261
/// - support of dynamic routes for metaUSD - #330
/// 1.2.0: support long routes up to 8 hops
/// 1.1.0: support long routes up to 6 hops
/// @author Alien Deployer (https://github.com/a17)
/// @author Jude (https://github.com/iammrjude)
/// @author JodsMigel (https://github.com/JodsMigel)
/// @author dvpublic (https://github.com/dvpublic)
contract Swapper is Controllable, ISwapper {
using SafeERC20 for IERC20;
using EnumerableSet for EnumerableSet.AddressSet;
//region ----- Constants -----
/// @dev Version of Swapper implementation
string public constant VERSION = "1.3.0";
uint public constant ROUTE_LENGTH_MAX = 8;
// keccak256(abi.encode(uint256(keccak256("erc7201:stability.Swapper")) - 1)) & ~bytes32(uint256(0xff));
bytes32 private constant SWAPPER_STORAGE_LOCATION =
0xa3f85328863358c70a5d8558b355ddce3bfd90131b6ba971b451f8def7c6e700;
//endregion ----- Constants -----
//region ----- Storage -----
/// @custom:storage-location erc7201:stability.Swapper
struct SwapperStorage {
mapping(address tokenIn => PoolData) pools;
mapping(address tokenIn => mapping(address tokenOut => PoolData)) blueChipsPools;
/// @inheritdoc ISwapper
mapping(address token => uint minAmountToSwap) threshold;
/// @dev Assets list.
EnumerableSet.AddressSet _assets;
/// @dev Blue Chip Assets list.
EnumerableSet.AddressSet _bcAssets;
}
//endregion -- Storage -----
function initialize(address platform_) public initializer {
__Controllable_init(platform_);
}
//region ----- Restricted actions -----
/// @inheritdoc ISwapper
function addPools(PoolData[] memory pools_, bool rewrite) external onlyOperator {
SwapperStorage storage $ = _getStorage();
uint len = pools_.length;
// nosemgrep
for (uint i; i < len; ++i) {
PoolData memory pool = pools_[i];
// nosemgrep
if ($.pools[pool.tokenIn].pool != address(0) && !rewrite) {
revert AlreadyExist();
}
$.pools[pool.tokenIn] = pool;
bool assetAdded = $._assets.add(pool.tokenIn);
emit PoolAdded(pool, assetAdded);
}
}
/// @inheritdoc ISwapper
function addPools(AddPoolData[] memory pools_, bool rewrite) external onlyOperator {
SwapperStorage storage $ = _getStorage();
uint len = pools_.length;
// nosemgrep
for (uint i; i < len; ++i) {
//slither-disable-next-line uninitialized-local
PoolData memory poolData;
poolData.pool = pools_[i].pool;
poolData.tokenIn = pools_[i].tokenIn;
poolData.tokenOut = pools_[i].tokenOut;
//slither-disable-next-line calls-loop
poolData.ammAdapter = IPlatform(platform()).ammAdapter(keccak256(bytes(pools_[i].ammAdapterId))).proxy;
if (poolData.ammAdapter == address(0)) {
revert UnknownAMMAdapter();
}
// nosemgrep
if ($.pools[poolData.tokenIn].pool != address(0) && !rewrite) {
revert AlreadyExist();
}
$.pools[poolData.tokenIn] = poolData;
bool assetAdded = $._assets.add(poolData.tokenIn);
emit PoolAdded(poolData, assetAdded);
}
}
function removePool(address token) external onlyOperator {
SwapperStorage storage $ = _getStorage();
delete $.pools[token];
//slither-disable-next-line unused-return
$._assets.remove(token);
emit PoolRemoved(token);
}
/// @inheritdoc ISwapper
function addBlueChipsPools(PoolData[] memory pools_, bool rewrite) external onlyOperator {
SwapperStorage storage $ = _getStorage();
uint len = pools_.length;
// nosemgrep
for (uint i; i < len; ++i) {
PoolData memory pool = pools_[i];
// nosemgrep
if ($.blueChipsPools[pool.tokenIn][pool.tokenOut].pool != address(0) && !rewrite) {
revert AlreadyExist();
}
$.blueChipsPools[pool.tokenIn][pool.tokenOut] = pool;
$.blueChipsPools[pool.tokenOut][pool.tokenIn] = pool;
_addBcAsset(pool.tokenIn);
_addBcAsset(pool.tokenOut);
emit BlueChipAdded(pool);
}
}
/// @inheritdoc ISwapper
function addBlueChipsPools(AddPoolData[] memory pools_, bool rewrite) external onlyOperator {
SwapperStorage storage $ = _getStorage();
uint len = pools_.length;
// nosemgrep
for (uint i; i < len; ++i) {
//slither-disable-next-line uninitialized-local
PoolData memory poolData;
poolData.pool = pools_[i].pool;
poolData.tokenIn = pools_[i].tokenIn;
poolData.tokenOut = pools_[i].tokenOut;
//slither-disable-next-line calls-loop
poolData.ammAdapter = IPlatform(platform()).ammAdapter(keccak256(bytes(pools_[i].ammAdapterId))).proxy;
if (poolData.ammAdapter == address(0)) {
revert UnknownAMMAdapter();
}
// nosemgrep
if ($.blueChipsPools[poolData.tokenIn][poolData.tokenOut].pool != address(0) && !rewrite) {
revert AlreadyExist();
}
$.blueChipsPools[poolData.tokenIn][poolData.tokenOut] = poolData;
$.blueChipsPools[poolData.tokenOut][poolData.tokenIn] = poolData;
_addBcAsset(poolData.tokenIn);
_addBcAsset(poolData.tokenOut);
emit BlueChipAdded(poolData);
}
}
function removeBlueChipPool(address tokenIn, address tokenOut) external onlyOperator {
SwapperStorage storage $ = _getStorage();
delete $.blueChipsPools[tokenIn][tokenOut];
if (!$._bcAssets.remove(tokenIn)) {
revert NotExist();
}
// do not remove tokenOut, assume tha tokenIn is the main target for the removing
emit BlueChipPoolRemoved(tokenIn, tokenOut);
}
/// @inheritdoc ISwapper
function setThresholds(address[] memory tokenIn, uint[] memory thresholdAmount) external onlyOperator {
SwapperStorage storage $ = _getStorage();
uint tokenInLen = tokenIn.length;
uint thresholdAmountLen = thresholdAmount.length;
if (tokenInLen != thresholdAmountLen) {
revert IControllable.IncorrectArrayLength();
}
// nosemgrep
for (uint i = 0; i < tokenInLen; ++i) {
$.threshold[tokenIn[i]] = thresholdAmount[i];
}
emit ThresholdChanged(tokenIn, thresholdAmount);
}
//endregion -- Restricted actions ----
//region ----- User actions -----
/// @dev Sell tokenIn for tokenOut. Assume approve on this contract exist.
function swap(address tokenIn, address tokenOut, uint amount, uint priceImpactTolerance) external {
SwapperStorage storage $ = _getStorage();
(PoolData[] memory route, string memory errorMessage) = buildRoute(tokenIn, tokenOut);
if (route.length == 0) {
revert(errorMessage);
}
uint thresholdTokenIn = $.threshold[tokenIn];
if (amount < thresholdTokenIn) {
revert LessThenThreshold(thresholdTokenIn);
}
_swap(route, amount, priceImpactTolerance);
}
/// @inheritdoc ISwapper
function swapWithRoute(PoolData[] memory route, uint amount, uint priceImpactTolerance) external {
_swap(route, amount, priceImpactTolerance);
}
//endregion -- User actions -----
//region ----- View functions -----
/// @inheritdoc ISwapper
function assets() external view returns (address[] memory) {
SwapperStorage storage $ = _getStorage();
return $._assets.values();
}
/// @inheritdoc ISwapper
function bcAssets() external view returns (address[] memory) {
SwapperStorage storage $ = _getStorage();
return $._bcAssets.values();
}
/// @inheritdoc ISwapper
function allAssets() external view returns (address[] memory) {
SwapperStorage storage $ = _getStorage();
address[] memory __bcAssets = $._bcAssets.values();
uint bcAssetsLen = __bcAssets.length;
address[] memory __assets = $._assets.values();
uint assetsLen = __assets.length;
uint total = bcAssetsLen;
uint i;
for (; i < assetsLen; ++i) {
if (!$._bcAssets.contains(__assets[i])) {
++total;
}
}
address[] memory _allAssets = new address[](total);
// nosemgrep
for (i = 0; i < bcAssetsLen; ++i) {
_allAssets[i] = __bcAssets[i];
}
// nosemgrep
for (uint k; k < assetsLen; ++k) {
if (!$._bcAssets.contains(__assets[k])) {
_allAssets[i] = __assets[k];
++i;
}
}
return _allAssets;
}
/// @inheritdoc ISwapper
function getPrice(address tokenIn, address tokenOut, uint amount) external view returns (uint) {
//slither-disable-next-line unused-return
(PoolData[] memory route,) = buildRoute(tokenIn, tokenOut);
if (route.length == 0) {
return 0;
}
uint price;
if (amount != 0) {
price = amount;
} else {
price = 10 ** IERC20Metadata(tokenIn).decimals();
}
uint len = route.length;
// nosemgrep
for (uint i; i < len; ++i) {
PoolData memory data = route[i];
//slither-disable-next-line calls-loop
price = IAmmAdapter(data.ammAdapter).getPrice(data.pool, data.tokenIn, data.tokenOut, price);
}
return price;
}
/// @inheritdoc ISwapper
function getPriceForRoute(PoolData[] memory route, uint amount) external view returns (uint) {
uint price;
if (amount != 0) {
price = amount;
} else {
price = 10 ** IERC20Metadata(route[0].tokenIn).decimals();
}
uint len = route.length;
// nosemgrep
for (uint i; i < len; ++i) {
PoolData memory data = route[i];
//slither-disable-next-line calls-loop
price = IAmmAdapter(data.ammAdapter).getPrice(data.pool, data.tokenIn, data.tokenOut, price);
}
return price;
}
/// @inheritdoc ISwapper
function isRouteExist(address tokenIn, address tokenOut) external view returns (bool) {
//slither-disable-next-line unused-return
(PoolData[] memory route,) = buildRoute(tokenIn, tokenOut);
return route.length != 0;
}
/// @inheritdoc ISwapper
function buildRoute(
address tokenIn,
address tokenOut
) public view override returns (PoolData[] memory route, string memory errorMessage) {
SwapperStorage storage $ = _getStorage();
route = new PoolData[](ROUTE_LENGTH_MAX);
// --- BLUE CHIPS for in/out
// in case that we try to liquidate blue chips use bc lps directly
PoolData memory poolDataBC = $.blueChipsPools[tokenIn][tokenOut];
if (poolDataBC.pool != address(0)) {
poolDataBC.tokenIn = tokenIn;
poolDataBC.tokenOut = tokenOut;
route[0] = poolDataBC;
return (_cutRoute(route, 1), "");
}
// --- POOL for in
// find the best Pool for token IN
PoolData memory poolDataIn = _getPoolData($, tokenIn, true);
if (poolDataIn.pool == address(0)) {
return (_cutRoute(route, 0), "Swapper: Not found pool for tokenIn");
}
route[0] = poolDataIn;
// if the best Pool for token IN a pair with token OUT token we complete the route
if (poolDataIn.tokenOut == tokenOut) {
return (_cutRoute(route, 1), "");
}
// --- BC for POOL_in
// if we able to swap opposite token to a blue chip it is the cheaper way to liquidate
poolDataBC = $.blueChipsPools[poolDataIn.tokenOut][tokenOut];
if (poolDataBC.pool != address(0)) {
poolDataBC.tokenIn = poolDataIn.tokenOut;
poolDataBC.tokenOut = tokenOut;
route[1] = poolDataBC;
return (_cutRoute(route, 2), "");
}
// --- POOL for out
// find the largest pool for token out
PoolData memory poolDataOut = _getPoolData($, tokenOut, false);
if (poolDataOut.pool == address(0)) {
return (_cutRoute(route, 0), "Swapper: Not found pool for tokenOut");
}
// if the largest pool for tokenOut contains tokenIn it is the best way
if (tokenIn == poolDataOut.tokenIn) {
route[0] = poolDataOut;
return (_cutRoute(route, 1), "");
}
// if we can swap between largest pools the route is ended
if (poolDataIn.tokenOut == poolDataOut.tokenIn) {
route[1] = poolDataOut;
return (_cutRoute(route, 2), "");
}
// --- BC for POOL_out
// if we able to swap opposite token to a blue chip it is the cheaper way to liquidate
poolDataBC = $.blueChipsPools[poolDataIn.tokenOut][poolDataOut.tokenIn];
if (poolDataBC.pool != address(0)) {
poolDataBC.tokenIn = poolDataIn.tokenOut;
poolDataBC.tokenOut = poolDataOut.tokenIn;
route[1] = poolDataBC;
route[2] = poolDataOut;
return (_cutRoute(route, 3), "");
}
// ------------------------------------------------------------------------
// RECURSIVE PART
// We don't have 1-2 pair routes. Need to find pairs for pairs.
// This part could be build as recursion but for reduce complexity and safe gas was not.
// ------------------------------------------------------------------------
// --- POOL2 for in
PoolData memory poolDataIn2 = _getPoolData($, poolDataIn.tokenOut, true);
if (poolDataIn2.pool == address(0)) {
return (_cutRoute(route, 0), "L: Not found pool for tokenIn2");
}
route[1] = poolDataIn2;
if (poolDataIn2.tokenOut == tokenOut) {
return (_cutRoute(route, 2), "");
}
if (poolDataIn2.tokenOut == poolDataOut.tokenIn) {
route[2] = poolDataOut;
return (_cutRoute(route, 3), "");
}
// --- BC for POOL2_in
poolDataBC = $.blueChipsPools[poolDataIn2.tokenOut][tokenOut];
if (poolDataBC.pool != address(0)) {
poolDataBC.tokenIn = poolDataIn2.tokenOut;
poolDataBC.tokenOut = tokenOut;
route[2] = poolDataBC;
return (_cutRoute(route, 3), "");
}
// --- POOL2 for out
// find the largest pool for token out
PoolData memory poolDataOut2 = _getPoolData($, poolDataOut.tokenIn, false);
if (poolDataOut2.pool == address(0)) {
return (_cutRoute(route, 0), "L: Not found pool for tokenOut2");
}
// if we can swap between largest pools the route is ended
if (poolDataIn.tokenOut == poolDataOut2.tokenIn) {
route[1] = poolDataOut2;
route[2] = poolDataOut;
return (_cutRoute(route, 3), "");
}
if (poolDataIn2.tokenOut == poolDataOut2.tokenIn) {
route[2] = poolDataOut2;
route[3] = poolDataOut;
return (_cutRoute(route, 4), "");
}
// --- BC for POOL2_out
// token OUT pool can be paired with BC pool with token IN
poolDataBC = $.blueChipsPools[tokenIn][poolDataOut2.tokenIn];
if (poolDataBC.pool != address(0)) {
poolDataBC.tokenIn = tokenIn;
poolDataBC.tokenOut = poolDataOut2.tokenIn;
route[0] = poolDataBC;
route[1] = poolDataOut2;
route[2] = poolDataOut;
return (_cutRoute(route, 3), "");
}
poolDataBC = $.blueChipsPools[poolDataIn.tokenOut][poolDataOut2.tokenIn];
if (poolDataBC.pool != address(0)) {
poolDataBC.tokenIn = poolDataIn.tokenOut;
poolDataBC.tokenOut = poolDataOut2.tokenIn;
route[1] = poolDataBC;
route[2] = poolDataOut2;
route[3] = poolDataOut;
return (_cutRoute(route, 4), "");
}
poolDataBC = $.blueChipsPools[poolDataIn2.tokenOut][poolDataOut2.tokenIn];
if (poolDataBC.pool != address(0)) {
poolDataBC.tokenIn = poolDataIn2.tokenOut;
poolDataBC.tokenOut = poolDataOut2.tokenIn;
route[2] = poolDataBC;
route[3] = poolDataOut2;
route[4] = poolDataOut;
return (_cutRoute(route, 5), "");
}
// --- POOL3 for in
PoolData memory poolDataIn3 = _getPoolData($, poolDataIn2.tokenOut, true);
if (poolDataIn3.pool == address(0)) {
return (_cutRoute(route, 0), "L: Not found pool for tokenIn3");
}
route[2] = poolDataIn3;
if (poolDataIn3.tokenOut == tokenOut) {
return (_cutRoute(route, 3), "");
}
if (poolDataIn3.tokenOut == poolDataOut.tokenIn) {
route[3] = poolDataOut;
return (_cutRoute(route, 4), "");
}
if (poolDataIn3.tokenOut == poolDataOut2.tokenIn) {
route[3] = poolDataOut2;
route[4] = poolDataOut;
return (_cutRoute(route, 5), "");
}
// --- POOL3 for out
// find the largest pool for token out 2
PoolData memory poolDataOut3 = _getPoolData($, poolDataOut2.tokenIn, false);
if (poolDataOut3.pool == address(0)) {
return (_cutRoute(route, 0), "L: Not found pool for tokenOut3");
}
// if we can swap between largest pools the route is ended
if (poolDataIn.tokenOut == poolDataOut3.tokenIn) {
route[1] = poolDataOut3;
route[2] = poolDataOut2;
route[3] = poolDataOut;
return (_cutRoute(route, 4), "");
}
if (poolDataIn2.tokenOut == poolDataOut3.tokenIn) {
route[2] = poolDataOut3;
route[3] = poolDataOut2;
route[4] = poolDataOut;
return (_cutRoute(route, 5), "");
}
if (poolDataIn3.tokenOut == poolDataOut3.tokenIn) {
route[3] = poolDataOut3;
route[4] = poolDataOut2;
route[5] = poolDataOut;
return (_cutRoute(route, 6), "");
}
if (tokenIn == poolDataOut3.tokenIn) {
route[0] = poolDataOut3;
route[1] = poolDataOut2;
route[2] = poolDataOut;
return (_cutRoute(route, 3), "");
}
// --- POOL4 for in
PoolData memory poolDataIn4 = _getPoolData($, poolDataIn3.tokenOut, true);
if (poolDataIn4.pool == address(0)) {
return (_cutRoute(route, 0), "L: Not found pool for tokenIn4");
}
route[3] = poolDataIn4;
if (poolDataIn4.tokenOut == tokenOut) {
return (_cutRoute(route, 4), "");
}
if (poolDataIn4.tokenOut == poolDataOut.tokenIn) {
route[4] = poolDataOut;
return (_cutRoute(route, 5), "");
}
if (poolDataIn4.tokenOut == poolDataOut2.tokenIn) {
route[4] = poolDataOut2;
route[5] = poolDataOut;
return (_cutRoute(route, 6), "");
}
if (poolDataIn4.tokenOut == poolDataOut3.tokenIn) {
route[4] = poolDataOut3;
route[5] = poolDataOut2;
route[6] = poolDataOut;
return (_cutRoute(route, 7), "");
}
// --- POOL4 for out
// find the largest pool for token out 3
PoolData memory poolDataOut4 = _getPoolData($, poolDataOut3.tokenIn, false);
if (poolDataOut4.pool == address(0)) {
return (_cutRoute(route, 0), "L: Not found pool for tokenOut4");
}
// if we can swap between largest pools the route is ended
if (poolDataIn.tokenOut == poolDataOut4.tokenIn) {
route[1] = poolDataOut4;
route[2] = poolDataOut3;
route[3] = poolDataOut2;
route[4] = poolDataOut;
return (_cutRoute(route, 5), "");
}
if (poolDataIn2.tokenOut == poolDataOut4.tokenIn) {
route[2] = poolDataOut4;
route[3] = poolDataOut3;
route[4] = poolDataOut2;
route[5] = poolDataOut;
return (_cutRoute(route, 6), "");
}
if (poolDataIn3.tokenOut == poolDataOut4.tokenIn) {
route[3] = poolDataOut4;
route[4] = poolDataOut3;
route[5] = poolDataOut2;
route[6] = poolDataOut;
return (_cutRoute(route, 7), "");
}
if (poolDataIn4.tokenOut == poolDataOut4.tokenIn) {
route[4] = poolDataOut4;
route[5] = poolDataOut3;
route[6] = poolDataOut2;
route[7] = poolDataOut;
return (_cutRoute(route, 8), "");
}
if (tokenIn == poolDataOut4.tokenIn) {
route[0] = poolDataOut4;
route[1] = poolDataOut3;
route[2] = poolDataOut2;
route[3] = poolDataOut;
return (_cutRoute(route, 4), "");
}
// We are not handling other cases such as:
// - If a token has liquidity with specific token
// and this token also has liquidity only with specific token.
// This case never exist but could be implemented if requires.
return (_cutRoute(route, 0), "Swapper: swap path not found");
}
/// @inheritdoc ISwapper
function threshold(address token) external view returns (uint threshold_) {
SwapperStorage storage $ = _getStorage();
threshold_ = $.threshold[token];
}
/// @inheritdoc ISwapper
function blueChipsPools(address tokenIn, address tokenOut) external view returns (PoolData memory poolData) {
SwapperStorage storage $ = _getStorage();
poolData = $.blueChipsPools[tokenIn][tokenOut];
}
//endregion -- View functions -----
//region ----- Internal logic -----
/// @notice Fix dynamic route on the fly
function _getPoolData(
SwapperStorage storage $,
address token,
bool isTokenIn
) internal view returns (PoolData memory poolData) {
poolData = $.pools[token];
if (poolData.tokenIn == token) {
if (isTokenIn) {
if (poolData.tokenIn == poolData.tokenOut) {
// support of dynamic route: tokenOut is selected on the fly
IAmmAdapter ammAdapter = IAmmAdapter(poolData.ammAdapter);
if (keccak256(bytes(ammAdapter.ammAdapterId())) == keccak256(bytes(AmmAdapterIdLib.META_VAULT))) {
poolData.tokenOut = IMetaVaultAmmAdapter(address(ammAdapter)).assetForWithdraw(poolData.pool);
}
}
} else {
if (poolData.tokenIn == poolData.tokenOut) {
// support of dynamic route: tokenOut is selected on the fly
IAmmAdapter ammAdapter = IAmmAdapter(poolData.ammAdapter);
if (keccak256(bytes(ammAdapter.ammAdapterId())) == keccak256(bytes(AmmAdapterIdLib.META_VAULT))) {
// we assign tokenOut here because in/out tokens are swapped below
poolData.tokenOut = IMetaVaultAmmAdapter(address(ammAdapter)).assetForDeposit(poolData.pool);
}
}
// need to swap directions for tokenOut pool
(poolData.tokenIn, poolData.tokenOut) = (poolData.tokenOut, poolData.tokenIn);
}
}
}
//slither-disable-next-line reentrancy-events
function _swap(PoolData[] memory route, uint amount, uint priceImpactTolerance) internal {
if (route.length == 0) {
revert IControllable.IncorrectArrayLength();
}
uint routeLength = route.length;
// nosemgrep
for (uint i; i < routeLength; i++) {
PoolData memory data = route[i];
// if it is the first step send tokens to the swapper from the current contract
if (i == 0) {
IERC20(data.tokenIn).safeTransferFrom(msg.sender, data.ammAdapter, amount);
}
address recipient;
// if it is not the last step of the route send to the next swapper
if (i != routeLength - 1) {
recipient = route[i + 1].ammAdapter;
} else {
// if it is the last step need to send to the sender
recipient = msg.sender;
}
// if it is the last step of the route we need to check if we have enough price impact tolerance
IAmmAdapter(data.ammAdapter).swap(data.pool, data.tokenIn, data.tokenOut, recipient, priceImpactTolerance);
}
emit Swap(route[0].tokenIn, route[routeLength - 1].tokenOut, amount);
}
function _cutRoute(PoolData[] memory route, uint length) internal pure returns (PoolData[] memory) {
PoolData[] memory result = new PoolData[](length);
// nosemgrep
for (uint i; i < length; ++i) {
result[i] = route[i];
}
return _removeCycling(result);
}
/// @notice #261: Detect and remove cycling in the route
function _removeCycling(PoolData[] memory data) internal pure returns (PoolData[] memory result) {
result = data;
if (data.length >= 4) {
if (
data[0].tokenIn == data[1].tokenOut && data[0].tokenOut == data[1].tokenIn
&& data[2].tokenIn == data[0].tokenIn // for safety
) {
// exclude first two pools
result = new PoolData[](data.length - 2);
for (uint i = 2; i < data.length; ++i) {
result[i - 2] = data[i];
}
}
}
}
function _addBcAsset(address asset) internal {
SwapperStorage storage $ = _getStorage();
if (!$._bcAssets.contains(asset)) {
//slither-disable-next-line unused-return
$._bcAssets.add(asset);
}
}
function _getStorage() private pure returns (SwapperStorage storage $) {
//slither-disable-next-line assembly
assembly {
$.slot := SWAPPER_STORAGE_LOCATION
}
}
//endregion ----- Internal logic -----
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
library AmmAdapterIdLib {
string public constant UNISWAPV3 = "UniswapV3";
string public constant ALGEBRA = "Algebra";
string public constant KYBER = "KyberSwap";
string public constant CURVE = "Curve";
string public constant SOLIDLY = "Solidly";
string public constant BALANCER_COMPOSABLE_STABLE = "BalancerComposableStable";
string public constant BALANCER_WEIGHTED = "BalancerWeighted";
string public constant ALGEBRA_V4 = "AlgebraV4";
string public constant ERC_4626 = "ERC4626";
string public constant BALANCER_V3_STABLE = "BalancerV3Stable";
string public constant PENDLE = "Pendle";
string public constant META_VAULT = "MetaUSD";
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IAmmAdapter} from "./IAmmAdapter.sol";
/// @dev Get price, swap, liquidity calculations. Used by strategies and swapper
/// @author dvpublic (https://github.com/dvpublic)
interface IMetaVaultAmmAdapter is IAmmAdapter {
/// @notice Asset in MetaVault.vaultForDeposit
function assetForDeposit(address pool) external view returns (address);
/// @notice Asset in MetaVault.vaultForWithdraw
function assetForWithdraw(address pool) external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {SlotsLib} from "../libs/SlotsLib.sol";
import {IControllable} from "../../interfaces/IControllable.sol";
import {IPlatform} from "../../interfaces/IPlatform.sol";
/// @dev Base core contract.
/// It store an immutable platform proxy address in the storage and provides access control to inherited contracts.
/// @author Alien Deployer (https://github.com/a17)
/// @author 0xhokugava (https://github.com/0xhokugava)
abstract contract Controllable is Initializable, IControllable, ERC165 {
using SlotsLib for bytes32;
string public constant CONTROLLABLE_VERSION = "1.0.1";
bytes32 internal constant _PLATFORM_SLOT = bytes32(uint(keccak256("eip1967.controllable.platform")) - 1);
bytes32 internal constant _CREATED_BLOCK_SLOT = bytes32(uint(keccak256("eip1967.controllable.created_block")) - 1);
/// @dev Prevent implementation init
constructor() {
_disableInitializers();
}
/// @notice Initialize contract after setup it as proxy implementation
/// Save block.timestamp in the "created" variable
/// @dev Use it only once after first logic setup
/// @param platform_ Platform address
//slither-disable-next-line naming-convention
function __Controllable_init(address platform_) internal onlyInitializing {
require(platform_ != address(0) && IPlatform(platform_).multisig() != address(0), IncorrectZeroArgument());
SlotsLib.set(_PLATFORM_SLOT, platform_); // syntax for forge coverage
_CREATED_BLOCK_SLOT.set(block.number);
emit ContractInitialized(platform_, block.timestamp, block.number);
}
modifier onlyGovernance() {
_requireGovernance();
_;
}
modifier onlyMultisig() {
_requireMultisig();
_;
}
modifier onlyGovernanceOrMultisig() {
_requireGovernanceOrMultisig();
_;
}
modifier onlyOperator() {
_requireOperator();
_;
}
modifier onlyFactory() {
_requireFactory();
_;
}
// ************* SETTERS/GETTERS *******************
/// @inheritdoc IControllable
function platform() public view override returns (address) {
return _PLATFORM_SLOT.getAddress();
}
/// @inheritdoc IControllable
function createdBlock() external view override returns (uint) {
return _CREATED_BLOCK_SLOT.getUint();
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IControllable).interfaceId || super.supportsInterface(interfaceId);
}
function _requireGovernance() internal view {
require(IPlatform(platform()).governance() == msg.sender, NotGovernance());
}
function _requireMultisig() internal view {
require(IPlatform(platform()).multisig() == msg.sender, NotMultisig());
}
function _requireGovernanceOrMultisig() internal view {
IPlatform _platform = IPlatform(platform());
require(
_platform.governance() == msg.sender || _platform.multisig() == msg.sender, NotGovernanceAndNotMultisig()
);
}
function _requireOperator() internal view {
require(IPlatform(platform()).isOperator(msg.sender), NotOperator());
}
function _requireFactory() internal view {
require(IPlatform(platform()).factory() == msg.sender, NotFactory());
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
/// @dev Get price, swap, liquidity calculations. Used by strategies and swapper
/// @author Alien Deployer (https://github.com/a17)
/// @author JodsMigel (https://github.com/JodsMigel)
interface IAmmAdapter is IERC165 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
error PriceIncreased();
error WrongCallbackAmount();
error NotSupportedByCAMM();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
event SwapInPool(
address pool,
address tokenIn,
address tokenOut,
address recipient,
uint priceImpactTolerance,
uint amountIn,
uint amountOut
);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DATA TYPES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
struct SwapCallbackData {
address tokenIn;
uint amount;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* VIEW FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice String ID of the adapter
function ammAdapterId() external view returns (string memory);
/// @notice Tokens of a pool supported by the adapter
function poolTokens(address pool) external view returns (address[] memory);
/// @notice Computes the maximum amount of liquidity received for given amounts of pool assets and the current
/// pool price.
/// This function signature can be used only for non-concentrated AMMs.
/// @param pool Address of a pool supported by the adapter
/// @param amounts Amounts of pool assets
/// @return liquidity Liquidity out value
/// @return amountsConsumed Amounts of consumed assets when providing liquidity
function getLiquidityForAmounts(
address pool,
uint[] memory amounts
) external view returns (uint liquidity, uint[] memory amountsConsumed);
/// @notice Priced proportions of pool assets
/// @param pool Address of a pool supported by the adapter
/// @return Proportions with 18 decimals precision. Max is 1e18, min is 0.
function getProportions(address pool) external view returns (uint[] memory);
/// @notice Current price in pool without amount impact
/// @param pool Address of a pool supported by the adapter
/// @param tokenIn Token for sell
/// @param tokenOut Token for buy
/// @param amount Amount of tokenIn. For zero value provided amount 1.0 (10 ** decimals of tokenIn) will be used.
/// @return Amount of tokenOut with tokenOut decimals precision
function getPrice(address pool, address tokenIn, address tokenOut, uint amount) external view returns (uint);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* WRITE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Swap given tokenIn for tokenOut. Assume that tokenIn already sent to this contract.
/// @param pool Address of a pool supported by the adapter
/// @param tokenIn Token for sell
/// @param tokenOut Token for buy
/// @param recipient Recipient for tokenOut
/// @param priceImpactTolerance Price impact tolerance. Must include fees at least. Denominator is 100_000.
function swap(
address pool,
address tokenIn,
address tokenOut,
address recipient,
uint priceImpactTolerance
) external;
/// @dev Initializer for proxied adapter
function init(address platform) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/// @dev Base core interface implemented by most platform contracts.
/// Inherited contracts store an immutable Platform proxy address in the storage,
/// which provides authorization capabilities and infrastructure contract addresses.
/// @author Alien Deployer (https://github.com/a17)
/// @author JodsMigel (https://github.com/JodsMigel)
/// @author dvpublic (https://github.com/dvpublic)
interface IControllable {
//region ----- Custom Errors -----
error IncorrectZeroArgument();
error IncorrectMsgSender();
error NotGovernance();
error NotMultisig();
error NotGovernanceAndNotMultisig();
error NotOperator();
error NotFactory();
error NotPlatform();
error NotVault();
error IncorrectArrayLength();
error AlreadyExist();
error NotExist();
error NotTheOwner();
error ETHTransferFailed();
error IncorrectInitParams();
error InsufficientBalance();
error IncorrectLtv(uint ltv);
error TooLowValue(uint value);
error IncorrectAssetsList(address[] assets_, address[] expectedAssets_);
//endregion -- Custom Errors -----
event ContractInitialized(address platform, uint ts, uint block);
/// @notice Stability Platform main contract address
function platform() external view returns (address);
/// @notice Version of contract implementation
/// @dev SemVer scheme MAJOR.MINOR.PATCH
//slither-disable-next-line naming-convention
function VERSION() external view returns (string memory);
/// @notice Block number when contract was initialized
function createdBlock() external view returns (uint);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/// @notice Interface of the main contract and entry point to the platform.
/// @author Alien Deployer (https://github.com/a17)
/// @author Jude (https://github.com/iammrjude)
/// @author JodsMigel (https://github.com/JodsMigel)
interface IPlatform {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
error AlreadyAnnounced();
error SameVersion();
error NoNewVersion();
error UpgradeTimerIsNotOver(uint TimerTimestamp);
error IncorrectFee(uint minFee, uint maxFee);
error NotEnoughAllowedBBToken();
error TokenAlreadyExistsInSet(address token);
error AggregatorNotExists(address dexAggRouter);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
event PlatformVersion(string version);
event UpgradeAnnounce(
string oldVersion, string newVersion, address[] proxies, address[] newImplementations, uint timelock
);
event CancelUpgrade(string oldVersion, string newVersion);
event ProxyUpgraded(
address indexed proxy, address implementation, string oldContractVersion, string newContractVersion
);
event Addresses(
address multisig_,
address factory_,
address priceReader_,
address swapper_,
address buildingPermitToken_,
address vaultManager_,
address strategyLogic_,
address aprOracle_,
address hardWorker,
address rebalancer,
address zap,
address bridge
);
event OperatorAdded(address operator);
event OperatorRemoved(address operator);
event FeesChanged(uint fee, uint feeShareVaultManager, uint feeShareStrategyLogic, uint feeShareEcosystem);
event MinInitialBoostChanged(uint minInitialBoostPerDay, uint minInitialBoostDuration);
event NewAmmAdapter(string id, address proxy);
event EcosystemRevenueReceiver(address receiver);
event SetAllowedBBTokenVaults(address bbToken, uint vaultsToBuild, bool firstSet);
event RemoveAllowedBBToken(address bbToken);
event AddAllowedBoostRewardToken(address token);
event RemoveAllowedBoostRewardToken(address token);
event AddDefaultBoostRewardToken(address token);
event RemoveDefaultBoostRewardToken(address token);
event AddBoostTokens(address[] allowedBoostRewardToken, address[] defaultBoostRewardToken);
event AllowedBBTokenVaultUsed(address bbToken, uint vaultToUse);
event AddDexAggregator(address router);
event RemoveDexAggregator(address router);
event MinTvlForFreeHardWorkChanged(uint oldValue, uint newValue);
event CustomVaultFee(address vault, uint platformFee);
event Rebalancer(address rebalancer_);
event Bridge(address bridge_);
event RevenueRouter(address revenueRouter_);
event MetaVaultFactory(address metaVaultFactory);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DATA TYPES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
struct PlatformUpgrade {
string newVersion;
address[] proxies;
address[] newImplementations;
}
struct PlatformSettings {
string networkName;
bytes32 networkExtra;
uint fee;
uint feeShareVaultManager;
uint feeShareStrategyLogic;
uint feeShareEcosystem;
uint minInitialBoostPerDay;
uint minInitialBoostDuration;
}
struct AmmAdapter {
string id;
address proxy;
}
struct SetupAddresses {
address factory;
address priceReader;
address swapper;
address buildingPermitToken;
address buildingPayPerVaultToken;
address vaultManager;
address strategyLogic;
address aprOracle;
address targetExchangeAsset;
address hardWorker;
address zap;
address revenueRouter;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* VIEW FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Platform version in CalVer scheme: YY.MM.MINOR-tag. Updates on core contract upgrades.
function platformVersion() external view returns (string memory);
/// @notice Time delay for proxy upgrades of core contracts and changing important platform settings by multisig
//slither-disable-next-line naming-convention
function TIME_LOCK() external view returns (uint);
/// @notice DAO governance
function governance() external view returns (address);
/// @notice Core team multi signature wallet. Development and operations fund
function multisig() external view returns (address);
/// @notice This NFT allow user to build limited number of vaults per week
function buildingPermitToken() external view returns (address);
/// @notice This ERC20 token is used as payment token for vault building
function buildingPayPerVaultToken() external view returns (address);
/// @notice Receiver of ecosystem revenue
function ecosystemRevenueReceiver() external view returns (address);
/// @dev The best asset in a network for swaps between strategy assets and farms rewards assets
/// The target exchange asset is used for finding the best strategy's exchange asset.
/// Rhe fewer routes needed to swap to the target exchange asset, the better.
function targetExchangeAsset() external view returns (address);
/// @notice Platform factory assembling vaults. Stores settings, strategy logic, farms.
/// Provides the opportunity to upgrade vaults and strategies.
/// @return Address of Factory proxy
function factory() external view returns (address);
/// @notice The holders of these NFT receive a share of the vault revenue
/// @return Address of VaultManager proxy
function vaultManager() external view returns (address);
/// @notice The holders of these tokens receive a share of the revenue received in all vaults using this strategy logic.
function strategyLogic() external view returns (address);
/// @notice Combining oracle and DeX spot prices
/// @return Address of PriceReader proxy
function priceReader() external view returns (address);
/// @notice Providing underlying assets APRs on-chain
/// @return Address of AprOracle proxy
function aprOracle() external view returns (address);
/// @notice On-chain price quoter and swapper
/// @return Address of Swapper proxy
function swapper() external view returns (address);
/// @notice HardWork resolver and caller
/// @return Address of HardWorker proxy
function hardWorker() external view returns (address);
/// @notice Rebalance resolver
/// @return Address of Rebalancer proxy
function rebalancer() external view returns (address);
/// @notice ZAP feature
/// @return Address of Zap proxy
function zap() external view returns (address);
/// @notice Platform revenue distributor
/// @return Address of the revenue distributor proxy
function revenueRouter() external view returns (address);
/// @notice Factory of MetaVaults
/// @return Address of the MetaVault factory
function metaVaultFactory() external view returns (address);
/// @notice Name of current EVM network
function networkName() external view returns (string memory);
/// @notice Minimal initial boost rewards per day USD amount which needs to create rewarding vault
function minInitialBoostPerDay() external view returns (uint);
/// @notice Minimal boost rewards vesting duration for initial boost
function minInitialBoostDuration() external view returns (uint);
/// @notice This function provides the timestamp of the platform upgrade timelock.
/// @dev This function is an external view function, meaning it doesn't modify the state.
/// @return uint representing the timestamp of the platform upgrade timelock.
function platformUpgradeTimelock() external view returns (uint);
/// @dev Extra network data
/// @return 0-2 bytes - color
/// 3-5 bytes - background color
/// 6-31 bytes - free
function networkExtra() external view returns (bytes32);
/// @notice Pending platform upgrade data
function pendingPlatformUpgrade() external view returns (PlatformUpgrade memory);
/// @notice Get platform revenue fee settings
/// @return fee Revenue fee % (between MIN_FEE - MAX_FEE) with DENOMINATOR precision.
/// @return feeShareVaultManager Revenue fee share % of VaultManager tokenId owner
/// @return feeShareStrategyLogic Revenue fee share % of StrategyLogic tokenId owner
/// @return feeShareEcosystem Revenue fee share % of ecosystemFeeReceiver
function getFees()
external
view
returns (uint fee, uint feeShareVaultManager, uint feeShareStrategyLogic, uint feeShareEcosystem);
/// @notice Get custom vault platform fee
/// @return fee revenue fee % with DENOMINATOR precision
function getCustomVaultFee(address vault) external view returns (uint fee);
/// @notice Platform settings
function getPlatformSettings() external view returns (PlatformSettings memory);
/// @notice AMM adapters of the platform
function getAmmAdapters() external view returns (string[] memory id, address[] memory proxy);
/// @notice Get AMM adapter data by hash
/// @param ammAdapterIdHash Keccak256 hash of adapter ID string
/// @return ID string and proxy address of AMM adapter
function ammAdapter(bytes32 ammAdapterIdHash) external view returns (AmmAdapter memory);
/// @notice Allowed buy-back tokens for rewarding vaults
function allowedBBTokens() external view returns (address[] memory);
/// @notice Vaults building limit for buy-back token.
/// This limit decrements when a vault for BB-token is built.
/// @param token Allowed buy-back token
/// @return vaultsLimit Number of vaults that can be built for BB-token
function allowedBBTokenVaults(address token) external view returns (uint vaultsLimit);
/// @notice Vaults building limits for allowed buy-back tokens.
/// @return bbToken Allowed buy-back tokens
/// @return vaultsLimit Number of vaults that can be built for BB-tokens
function allowedBBTokenVaults() external view returns (address[] memory bbToken, uint[] memory vaultsLimit);
/// @notice Non-zero vaults building limits for allowed buy-back tokens.
/// @return bbToken Allowed buy-back tokens
/// @return vaultsLimit Number of vaults that can be built for BB-tokens
function allowedBBTokenVaultsFiltered()
external
view
returns (address[] memory bbToken, uint[] memory vaultsLimit);
/// @notice Check address for existance in operators list
/// @param operator Address
/// @return True if this address is Stability Operator
function isOperator(address operator) external view returns (bool);
/// @notice Tokens that can be used for boost rewards of rewarding vaults
/// @return Addresses of tokens
function allowedBoostRewardTokens() external view returns (address[] memory);
/// @notice Allowed boost reward tokens that used for unmanaged rewarding vaults creation
/// @return Addresses of tokens
function defaultBoostRewardTokens() external view returns (address[] memory);
/// @notice Allowed boost reward tokens that used for unmanaged rewarding vaults creation
/// @param addressToRemove This address will be removed from default boost reward tokens
/// @return Addresses of tokens
function defaultBoostRewardTokensFiltered(address addressToRemove) external view returns (address[] memory);
/// @notice Allowed DeX aggregators
/// @return Addresses of DeX aggregator rounters
function dexAggregators() external view returns (address[] memory);
/// @notice DeX aggregator router address is allowed to be used in the platform
/// @param dexAggRouter Address of DeX aggreagator router
/// @return Can be used
function isAllowedDexAggregatorRouter(address dexAggRouter) external view returns (bool);
/// @notice Show minimum TVL for compensate if vault has not enough ETH
/// @return Minimum TVL for compensate.
function minTvlForFreeHardWork() external view returns (uint);
/// @notice Front-end platform viewer
/// @return platformAddresses Platform core addresses
/// platformAddresses[0] factory
/// platformAddresses[1] vaultManager
/// platformAddresses[2] strategyLogic
/// platformAddresses[3] buildingPermitToken
/// platformAddresses[4] buildingPayPerVaultToken
/// platformAddresses[5] governance
/// platformAddresses[6] multisig
/// platformAddresses[7] zap
/// platformAddresses[8] bridge
/// @return bcAssets Blue chip token addresses
/// @return dexAggregators_ DeX aggregators allowed to be used entire the platform
/// @return vaultType Vault type ID strings
/// @return vaultExtra Vault color, background color and other extra data. Index of vault same as in previous array.
/// @return vaultBulldingPrice Price of creating new vault in buildingPayPerVaultToken. Index of vault same as in previous array.
/// @return strategyId Strategy logic ID strings
/// @return isFarmingStrategy True if strategy is farming strategy. Index of strategy same as in previous array.
/// @return strategyTokenURI StrategyLogic NFT tokenId metadata and on-chain image. Index of strategy same as in previous array.
/// @return strategyExtra Strategy color, background color and other extra data. Index of strategy same as in previous array.
function getData()
external
view
returns (
address[] memory platformAddresses,
address[] memory bcAssets,
address[] memory dexAggregators_,
string[] memory vaultType,
bytes32[] memory vaultExtra,
uint[] memory vaultBulldingPrice,
string[] memory strategyId,
bool[] memory isFarmingStrategy,
string[] memory strategyTokenURI,
bytes32[] memory strategyExtra
);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* WRITE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Add platform operator.
/// Only governance and multisig can add operator.
/// @param operator Address of new operator
function addOperator(address operator) external;
/// @notice Remove platform operator.
/// Only governance and multisig can remove operator.
/// @param operator Address of operator to remove
function removeOperator(address operator) external;
/// @notice Announce upgrade of platform proxies implementations
/// Only governance and multisig can announce platform upgrades.
/// @param newVersion New platform version. Version must be changed when upgrading.
/// @param proxies Addresses of core contract proxies
/// @param newImplementations New implementation for proxy. Index of proxy same as in previous array.
function announcePlatformUpgrade(
string memory newVersion,
address[] memory proxies,
address[] memory newImplementations
) external;
/// @notice Upgrade platform
/// Only operator (multisig is operator too) can ececute pending platform upgrade
function upgrade() external;
/// @notice Cancel pending platform upgrade
/// Only operator (multisig is operator too) can ececute pending platform upgrade
function cancelUpgrade() external;
/// @notice Register AMM adapter in platform
/// @param id AMM adapter ID string from AmmAdapterIdLib
/// @param proxy Address of AMM adapter proxy
function addAmmAdapter(string memory id, address proxy) external;
// todo Only governance and multisig can set allowed bb-token vaults building limit
/// @notice Set new vaults building limit for buy-back token
/// @param bbToken Address of allowed buy-back token
/// @param vaultsToBuild Number of vaults that can be built for BB-token
function setAllowedBBTokenVaults(address bbToken, uint vaultsToBuild) external;
// todo Only governance and multisig can add allowed boost reward token
/// @notice Add new allowed boost reward token
/// @param token Address of token
function addAllowedBoostRewardToken(address token) external;
// todo Only governance and multisig can remove allowed boost reward token
/// @notice Remove allowed boost reward token
/// @param token Address of allowed boost reward token
function removeAllowedBoostRewardToken(address token) external;
// todo Only governance and multisig can add default boost reward token
/// @notice Add default boost reward token
/// @param token Address of default boost reward token
function addDefaultBoostRewardToken(address token) external;
// todo Only governance and multisig can remove default boost reward token
/// @notice Remove default boost reward token
/// @param token Address of allowed boost reward token
function removeDefaultBoostRewardToken(address token) external;
// todo Only governance and multisig can add allowed boost reward token
// todo Only governance and multisig can add default boost reward token
/// @notice Add new allowed boost reward token
/// @notice Add default boost reward token
/// @param allowedBoostRewardToken Address of allowed boost reward token
/// @param defaultBoostRewardToken Address of default boost reward token
function addBoostTokens(
address[] memory allowedBoostRewardToken,
address[] memory defaultBoostRewardToken
) external;
/// @notice Decrease allowed BB-token vault building limit when vault is built
/// Only Factory can do it.
/// @param bbToken Address of allowed buy-back token
function useAllowedBBTokenVault(address bbToken) external;
/// @notice Allow DeX aggregator routers to be used in the platform
/// @param dexAggRouter Addresses of DeX aggreagator routers
function addDexAggregators(address[] memory dexAggRouter) external;
/// @notice Remove allowed DeX aggregator router from the platform
/// @param dexAggRouter Address of DeX aggreagator router
function removeDexAggregator(address dexAggRouter) external;
/// @notice Change initial boost rewards settings
/// @param minInitialBoostPerDay_ Minimal initial boost rewards per day USD amount which needs to create rewarding vault
/// @param minInitialBoostDuration_ Minimal boost rewards vesting duration for initial boost
function setInitialBoost(uint minInitialBoostPerDay_, uint minInitialBoostDuration_) external;
/// @notice Update new minimum TVL for compensate.
/// @param value New minimum TVL for compensate.
function setMinTvlForFreeHardWork(uint value) external;
/// @notice Set custom platform fee for vault
/// @param vault Vault address
/// @param platformFee Custom platform fee
function setCustomVaultFee(address vault, uint platformFee) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
/// @notice On-chain price quoter and swapper by predefined routes
/// @author Alien Deployer (https://github.com/a17)
/// @author Jude (https://github.com/iammrjude)
/// @author JodsMigel (https://github.com/JodsMigel)
/// @author 0xhokugava (https://github.com/0xhokugava)
interface ISwapper {
event Swap(address indexed tokenIn, address indexed tokenOut, uint amount);
event PoolAdded(PoolData poolData, bool assetAdded);
event PoolRemoved(address token);
event BlueChipAdded(PoolData poolData);
event ThresholdChanged(address[] tokenIn, uint[] thresholdAmount);
event BlueChipPoolRemoved(address tokenIn, address tokenOut);
//region ----- Custom Errors -----
error UnknownAMMAdapter();
error LessThenThreshold(uint minimumAmount);
error NoRouteFound();
error NoRoutesForAssets();
//endregion -- Custom Errors -----
struct PoolData {
address pool;
address ammAdapter;
address tokenIn;
address tokenOut;
}
struct AddPoolData {
address pool;
string ammAdapterId;
address tokenIn;
address tokenOut;
}
/// @notice All assets in pools added to Swapper
/// @return Addresses of assets
function assets() external view returns (address[] memory);
/// @notice All blue chip assets in blue chip pools added to Swapper
/// @return Addresses of blue chip assets
function bcAssets() external view returns (address[] memory);
/// @notice All assets in Swapper
/// @return Addresses of assets and blue chip assets
function allAssets() external view returns (address[] memory);
/// @notice Add pools with largest TVL
/// @param pools Largest pools with AMM adapter addresses
/// @param rewrite Rewrite pool for tokenIn
function addPools(PoolData[] memory pools, bool rewrite) external;
/// @notice Add pools with largest TVL
/// @param pools Largest pools with AMM adapter ID string
/// @param rewrite Rewrite pool for tokenIn
function addPools(AddPoolData[] memory pools, bool rewrite) external;
/// @notice Add largest pools with the most popular tokens on the current network
/// @param pools_ PoolData array with pool, tokens and AMM adapter address
/// @param rewrite Change exist pool records
function addBlueChipsPools(PoolData[] memory pools_, bool rewrite) external;
/// @notice Add largest pools with the most popular tokens on the current network
/// @param pools_ AddPoolData array with pool, tokens and AMM adapter string ID
/// @param rewrite Change exist pool records
function addBlueChipsPools(AddPoolData[] memory pools_, bool rewrite) external;
/// @notice Retrieves pool data for a specified token swap in Blue Chip Pools.
/// @dev This function provides information about the pool associated with the specified input and output tokens.
/// @param tokenIn The input token address.
/// @param tokenOut The output token address.
/// @return poolData The data structure containing information about the Blue Chip Pool.
/// @custom:opcodes view
function blueChipsPools(address tokenIn, address tokenOut) external view returns (PoolData memory poolData);
/// @notice Set swap threshold for token
/// @dev Prevents dust swap.
/// @param tokenIn Swap input token
/// @param thresholdAmount Minimum amount of token for executing swap
function setThresholds(address[] memory tokenIn, uint[] memory thresholdAmount) external;
/// @notice Swap threshold for token
/// @param token Swap input token
/// @return threshold_ Minimum amount of token for executing swap
function threshold(address token) external view returns (uint threshold_);
/// @notice Price of given tokenIn against tokenOut
/// @param tokenIn Swap input token
/// @param tokenOut Swap output token
/// @param amount Amount of tokenIn. If provide zero then amount is 1.0.
/// @return Amount of tokenOut with decimals of tokenOut
function getPrice(address tokenIn, address tokenOut, uint amount) external view returns (uint);
/// @notice Return price the first poolData.tokenIn against the last poolData.tokenOut in decimals of tokenOut.
/// @param route Array of pool address, swapper address tokenIn, tokenOut
/// @param amount Amount of tokenIn. If provide zero then amount is 1.0.
function getPriceForRoute(PoolData[] memory route, uint amount) external view returns (uint);
/// @notice Check possibility of swap tokenIn for tokenOut
/// @param tokenIn Swap input token
/// @param tokenOut Swap output token
/// @return Swap route exists
function isRouteExist(address tokenIn, address tokenOut) external view returns (bool);
/// @notice Build route for swap. No reverts inside.
/// @param tokenIn Swap input token
/// @param tokenOut Swap output token
/// @return route Array of pools for swap tokenIn to tokenOut. Zero length indicate an error.
/// @return errorMessage Possible reason why the route was not found. Empty for success routes.
function buildRoute(
address tokenIn,
address tokenOut
) external view returns (PoolData[] memory route, string memory errorMessage);
/// @notice Sell tokenIn for tokenOut
/// @dev Assume approve on this contract exist
/// @param tokenIn Swap input token
/// @param tokenOut Swap output token
/// @param amount Amount of tokenIn for swap.
/// @param priceImpactTolerance Price impact tolerance. Must include fees at least. Denominator is 100_000.
function swap(address tokenIn, address tokenOut, uint amount, uint priceImpactTolerance) external;
/// @notice Swap by predefined route
/// @param route Array of pool address, swapper address tokenIn, tokenOut.
/// TokenIn from first item will be swaped to tokenOut of last .
/// @param amount Amount of first item tokenIn.
/// @param priceImpactTolerance Price impact tolerance. Must include fees at least. Denominator is 100_000.
function swapWithRoute(PoolData[] memory route, uint amount, uint priceImpactTolerance) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./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);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
/// @title Minimal library for setting / getting slot variables (used in upgradable proxy contracts)
library SlotsLib {
/// @dev Gets a slot as an address
function getAddress(bytes32 slot) internal view returns (address result) {
assembly {
result := sload(slot)
}
}
/// @dev Gets a slot as uint256
function getUint(bytes32 slot) internal view returns (uint result) {
assembly {
result := sload(slot)
}
}
/// @dev Sets a slot with address
/// @notice Check address for 0 at the setter
function set(bytes32 slot, address value) internal {
assembly {
sstore(slot, value)
}
}
/// @dev Sets a slot with uint
function set(bytes32 slot, uint value) internal {
assembly {
sstore(slot, value)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}{
"remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@solady/=lib/solady/src/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/",
"solady/=lib/solady/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"AlreadyExist","type":"error"},{"inputs":[],"name":"ETHTransferFailed","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"IncorrectArrayLength","type":"error"},{"inputs":[{"internalType":"address[]","name":"assets_","type":"address[]"},{"internalType":"address[]","name":"expectedAssets_","type":"address[]"}],"name":"IncorrectAssetsList","type":"error"},{"inputs":[],"name":"IncorrectInitParams","type":"error"},{"inputs":[{"internalType":"uint256","name":"ltv","type":"uint256"}],"name":"IncorrectLtv","type":"error"},{"inputs":[],"name":"IncorrectMsgSender","type":"error"},{"inputs":[],"name":"IncorrectZeroArgument","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[{"internalType":"uint256","name":"minimumAmount","type":"uint256"}],"name":"LessThenThreshold","type":"error"},{"inputs":[],"name":"NoRouteFound","type":"error"},{"inputs":[],"name":"NoRoutesForAssets","type":"error"},{"inputs":[],"name":"NotExist","type":"error"},{"inputs":[],"name":"NotFactory","type":"error"},{"inputs":[],"name":"NotGovernance","type":"error"},{"inputs":[],"name":"NotGovernanceAndNotMultisig","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"NotMultisig","type":"error"},{"inputs":[],"name":"NotOperator","type":"error"},{"inputs":[],"name":"NotPlatform","type":"error"},{"inputs":[],"name":"NotTheOwner","type":"error"},{"inputs":[],"name":"NotVault","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"TooLowValue","type":"error"},{"inputs":[],"name":"UnknownAMMAdapter","type":"error"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"ammAdapter","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"indexed":false,"internalType":"struct ISwapper.PoolData","name":"poolData","type":"tuple"}],"name":"BlueChipAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"address","name":"tokenOut","type":"address"}],"name":"BlueChipPoolRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"platform","type":"address"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"block","type":"uint256"}],"name":"ContractInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"ammAdapter","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"indexed":false,"internalType":"struct ISwapper.PoolData","name":"poolData","type":"tuple"},{"indexed":false,"internalType":"bool","name":"assetAdded","type":"bool"}],"name":"PoolAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"PoolRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":true,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"tokenIn","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"thresholdAmount","type":"uint256[]"}],"name":"ThresholdChanged","type":"event"},{"inputs":[],"name":"CONTROLLABLE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROUTE_LENGTH_MAX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"string","name":"ammAdapterId","type":"string"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"internalType":"struct ISwapper.AddPoolData[]","name":"pools_","type":"tuple[]"},{"internalType":"bool","name":"rewrite","type":"bool"}],"name":"addBlueChipsPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"ammAdapter","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"internalType":"struct ISwapper.PoolData[]","name":"pools_","type":"tuple[]"},{"internalType":"bool","name":"rewrite","type":"bool"}],"name":"addBlueChipsPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"string","name":"ammAdapterId","type":"string"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"internalType":"struct ISwapper.AddPoolData[]","name":"pools_","type":"tuple[]"},{"internalType":"bool","name":"rewrite","type":"bool"}],"name":"addPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"ammAdapter","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"internalType":"struct ISwapper.PoolData[]","name":"pools_","type":"tuple[]"},{"internalType":"bool","name":"rewrite","type":"bool"}],"name":"addPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"allAssets","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"assets","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bcAssets","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"name":"blueChipsPools","outputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"ammAdapter","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"internalType":"struct ISwapper.PoolData","name":"poolData","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"name":"buildRoute","outputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"ammAdapter","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"internalType":"struct ISwapper.PoolData[]","name":"route","type":"tuple[]"},{"internalType":"string","name":"errorMessage","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"createdBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"ammAdapter","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"internalType":"struct ISwapper.PoolData[]","name":"route","type":"tuple[]"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getPriceForRoute","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"platform_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"name":"isRouteExist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platform","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"name":"removeBlueChipPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"removePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokenIn","type":"address[]"},{"internalType":"uint256[]","name":"thresholdAmount","type":"uint256[]"}],"name":"setThresholds","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":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"priceImpactTolerance","type":"uint256"}],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"ammAdapter","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"internalType":"struct ISwapper.PoolData[]","name":"route","type":"tuple[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"priceImpactTolerance","type":"uint256"}],"name":"swapWithRoute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"threshold","outputs":[{"internalType":"uint256","name":"threshold_","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b61458d806100d65f395ff3fe608060405234801561000f575f5ffd5b506004361061016d575f3560e01c806391a6ba43116100d9578063c6a9b3d411610093578063d87de3181161006e578063d87de3181461040c578063f97e42161461041f578063fe02915614610432578063ffa1ad7414610445575f5ffd5b8063c6a9b3d4146103aa578063c7b07d72146103b2578063c86ec2bf146103c5575f5ffd5b806391a6ba4314610281578063936725ec14610338578063951cbc6314610369578063a9dd14d61461037c578063ba6d313d1461038f578063c4d66de814610397575f5ffd5b80634bde38c81161012a5780634bde38c8146101fd578063570fa5bc1461021d5780635b504baf146102305780636e8811021461024357806371a973051461026457806376d708d714610279575f5ffd5b806301ffc9a714610171578063371d6587146101995780633a79f2c8146101ae5780633a85fcab146101c15780633b7d0946146101d45780634593144c146101e7575b5f5ffd5b61018461017f366004613a22565b610469565b60405190151581526020015b60405180910390f35b6101ac6101a7366004613a68565b61049f565b005b6101ac6101bc366004613b6b565b6105a4565b6101ac6101cf366004613b6b565b610862565b6101ac6101e2366004613cda565b610b9e565b6101ef610c67565b604051908152602001610190565b610205610c9f565b6040516001600160a01b039091168152602001610190565b6101ac61022b366004613d5a565b610cce565b6101ac61023e366004613ed5565b610db1565b610256610251366004613a68565b610dc1565b604051610190929190613f7f565b61026c612260565b604051610190919061401a565b61026c61229f565b61032b61028f366004613a68565b60408051608080820183525f808352602080840182905283850182905260609384018290526001600160a01b0396871682527fa3f85328863358c70a5d8558b355ddce3bfd90131b6ba971b451f8def7c6e7018152848220958716825294855283902083519182018452805486168252600181015486169482019490945260028401548516928101929092526003909201549092169082015290565b604051610190919061402c565b61035c60405180604001604052806005815260200164312e302e3160d81b81525081565b604051610190919061403a565b610184610377366004613a68565b612479565b6101ef61038a36600461404c565b612491565b6101ef600881565b6101ac6103a5366004613cda565b612602565b61026c61270e565b6101ac6103c036600461408a565b612734565b6101ef6103d3366004613cda565b6001600160a01b03165f9081527fa3f85328863358c70a5d8558b355ddce3bfd90131b6ba971b451f8def7c6e702602052604090205490565b6101ac61041a36600461408a565b612909565b6101ef61042d3660046140cd565b612a5a565b6101ac61044036600461410e565b612bc5565b61035c604051806040016040528060058152602001640312e332e360dc1b81525081565b5f6001600160e01b03198216630f1ec81f60e41b148061049957506301ffc9a760e01b6001600160e01b03198316145b92915050565b6104a7612c61565b6001600160a01b038281165f9081527fa3f85328863358c70a5d8558b355ddce3bfd90131b6ba971b451f8def7c6e7016020908152604080832093851683529290522080546001600160a01b0319908116825560018201805482169055600282018054821690556003909101805490911690555f5160206144d15f395f51905f5261053f5f5160206144f15f395f51905f5284612cf0565b61055c5760405163ad5679e160e01b815260040160405180910390fd5b604080516001600160a01b038086168252841660208201527f0244dbcda3b6229e4548013cf904734fe7f8e256767bb56343a05ae08e0eae2d910160405180910390a1505050565b6105ac612c61565b81515f5160206144d15f395f51905f52905f5b8181101561085b57604080516080810182525f8082526020820181905291810182905260608101919091528582815181106105fc576105fc614151565b6020908102919091010151516001600160a01b03168152855186908390811061062757610627614151565b60200260200101516040015181604001906001600160a01b031690816001600160a01b03168152505085828151811061066257610662614151565b60209081029190910101516060908101516001600160a01b031690820152610688610c9f565b6001600160a01b0316634254af1c8784815181106106a8576106a8614151565b602002602001015160200151805190602001206040518263ffffffff1660e01b81526004016106d991815260200190565b5f60405180830381865afa1580156106f3573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261071a91908101906141b2565b6020908101516001600160a01b031690820181905261074c576040516313ffb03f60e11b815260040160405180910390fd5b6040808201516001600160a01b039081165f908152602087905291909120541615801590610778575084155b15610796576040516333e9449d60e21b815260040160405180910390fd5b604081810180516001600160a01b039081165f9081526020888152938120855181546001600160a01b03199081169185169190911782559486015160018201805487169185169190911790559251600284018054861682851617905560608601516003948501805490961693169290921790935561081691870190612d04565b90507fea9169860adcad97f4b578ffd6a73fb02c6bf17502f0c4a724e9886009882eef8282604051610849929190614250565b60405180910390a150506001016105bf565b5050505050565b61086a612c61565b81515f5160206144d15f395f51905f52905f5b8181101561085b57604080516080810182525f8082526020820181905291810182905260608101919091528582815181106108ba576108ba614151565b6020908102919091010151516001600160a01b0316815285518690839081106108e5576108e5614151565b60200260200101516040015181604001906001600160a01b031690816001600160a01b03168152505085828151811061092057610920614151565b60209081029190910101516060908101516001600160a01b031690820152610946610c9f565b6001600160a01b0316634254af1c87848151811061096657610966614151565b602002602001015160200151805190602001206040518263ffffffff1660e01b815260040161099791815260200190565b5f60405180830381865afa1580156109b1573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526109d891908101906141b2565b6020908101516001600160a01b0316908201819052610a0a576040516313ffb03f60e11b815260040160405180910390fd5b6040808201516001600160a01b039081165f908152600187016020908152838220606086015184168352905291909120541615801590610a48575084155b15610a66576040516333e9449d60e21b815260040160405180910390fd5b604081810180516001600160a01b039081165f9081526001888101602081815286842060608901805187168652908252878520895181546001600160a01b0319908116918916919091178255838b01805183880180548416918b169190911790558951600280850180548516928c16929092179091558451600394850180548516918c16918217905589529585528a88208a518a16895290945298909520895181548a169088161781559151938201805489169487169490941790935594519085018054871682861617905590519390910180549094169290911691909117909155610b5190612d18565b610b5e8160600151612d18565b7fa99a974bf9b388be84d2af666c0e09a93de5d8cbecb06f32025cbcd1c14011b181604051610b8d919061402c565b60405180910390a15060010161087d565b610ba6612c61565b6001600160a01b0381165f9081525f5160206144d15f395f51905f526020819052604090912080546001600160a01b031990811682556001820180548216905560028201805482169055600390910180549091169055610c267fa3f85328863358c70a5d8558b355ddce3bfd90131b6ba971b451f8def7c6e70383612cf0565b506040516001600160a01b03831681527f4106dfdaa577573db51c0ca93f766dbedfa0758faa2e7f5bcdb7c142be803c3f9060200160405180910390a15050565b5f610c9a610c9660017f812a673dfca07956350df10f8a654925f561d7a0da09bdbe79e653939a14d9f1614281565b5490565b905090565b5f610c9a610c9660017faa116a42804728f23983458454b6eb9c6ddf3011db9f9addaf3cd7508d85b0d6614281565b610cd6612c61565b815181515f5160206144d15f395f51905f529190808214610d0a57604051630ef9926760e21b815260040160405180910390fd5b5f5b82811015610d7057848181518110610d2657610d26614151565b6020026020010151846002015f888481518110610d4557610d45614151565b6020908102919091018101516001600160a01b031682528101919091526040015f2055600101610d0c565b507f30e6061dda0fa9ccf5d58c384f3bacd9468b361b792ee40087e70fe248c5adaf8585604051610da2929190614294565b60405180910390a15050505050565b610dbc838383612d52565b505050565b604080516008808252610120820190925260609182915f5160206144d15f395f51905f5291602082015b604080516080810182525f8082526020808301829052928201819052606082015282525f19909201910181610deb5750506001600160a01b038681165f9081526001848101602090815260408084208a86168552825292839020835160808101855281548616808252938201548616928101929092526002810154851693820193909352600390920154909216606082015291945015610ee0576001600160a01b038087166040830152851660608201528351819085905f90610eb057610eb0614151565b6020026020010181905250610ec6846001612f3b565b60405180602001604052805f815250935093505050612259565b5f610eed83886001613004565b80519091506001600160a01b0316610f2e57610f09855f612f3b565b6040518060600160405280602381526020016145356023913994509450505050612259565b80855f81518110610f4157610f41614151565b6020026020010181905250856001600160a01b031681606001516001600160a01b031603610f8f57610f74856001612f3b565b60405180602001604052805f81525094509450505050612259565b6060808201516001600160a01b039081165f9081526001808701602090815260408084208c861685528252928390208351608081018552815486168082529382015486169281019290925260028101548516938201939093526003909201549092169281019290925290925015611049576060808201516001600160a01b039081166040850152871690830152845182908690600190811061103357611033614151565b6020026020010181905250610f74856002612f3b565b5f61105584885f613004565b80519091506001600160a01b031661109757611071865f612f3b565b604051806060016040528060248152602001614511602491399550955050505050612259565b80604001516001600160a01b0316886001600160a01b0316036110f95780865f815181106110c7576110c7614151565b60200260200101819052506110dd866001612f3b565b60405180602001604052805f8152509550955050505050612259565b80604001516001600160a01b031682606001516001600160a01b03160361114457808660018151811061112e5761112e614151565b60200260200101819052506110dd866002612f3b565b6060808301516001600160a01b039081165f90815260018088016020908152604080842087820151861685528252928390208351608081018552815486168082529382015486169281019290925260028101548516938201939093526003909201549092169281019290925290935015611226576060808301516001600160a01b03908116604080870191909152830151169084015285518390879060019081106111f1576111f1614151565b6020026020010181905250808660028151811061121057611210614151565b60200260200101819052506110dd866003612f3b565b5f6112378584606001516001613004565b80519091506001600160a01b031661129757611253875f612f3b565b6040518060400160405280601e81526020017f4c3a204e6f7420666f756e6420706f6f6c20666f7220746f6b656e496e320000815250965096505050505050612259565b80876001815181106112ab576112ab614151565b6020026020010181905250876001600160a01b031681606001516001600160a01b0316036112fb576112de876002612f3b565b60405180602001604052805f815250965096505050505050612259565b81604001516001600160a01b031681606001516001600160a01b03160361134657818760028151811061133057611330614151565b60200260200101819052506112de876003612f3b565b6060808201516001600160a01b039081165f9081526001808901602090815260408084208e8616855282529283902083516080810185528154861680825293820154861692810192909252600281015485169382019390935260039092015490921692810192909252909450156113ea576060808201516001600160a01b039081166040870152891690850152865184908890600290811061133057611330614151565b5f6113fa8684604001515f613004565b80519091506001600160a01b031661145b57611416885f612f3b565b6040518060400160405280601f81526020017f4c3a204e6f7420666f756e6420706f6f6c20666f7220746f6b656e4f7574320081525097509750505050505050612259565b80604001516001600160a01b031684606001516001600160a01b0316036114e357808860018151811061149057611490614151565b602002602001018190525082886002815181106114af576114af614151565b60200260200101819052506114c5886003612f3b565b60405180602001604052805f81525097509750505050505050612259565b80604001516001600160a01b031682606001516001600160a01b03160361154d57808860028151811061151857611518614151565b6020026020010181905250828860038151811061153757611537614151565b60200260200101819052506114c5886004612f3b565b6001600160a01b03808b165f90815260018089016020908152604080842086820151861685528252928390208351608081018552815486168082529382015486169281019290925260028101548516938201939093526003909201549092166060820152955015611608576001600160a01b03808b166040808801919091528201511660608601528751859089905f906115e9576115e9614151565b6020026020010181905250808860018151811061149057611490614151565b6060808501516001600160a01b039081165f9081526001808a0160209081526040808420878201518616855282529283902083516080810185528154861680825293820154861692810192909252600281015485169382019390935260039092015490921692810192909252909550156116d4576060808501516001600160a01b03908116604080890191909152830151169086015287518590899060019081106116b5576116b5614151565b6020026020010181905250808860028151811061151857611518614151565b6060808301516001600160a01b039081165f9081526001808a0160209081526040808420878201518616855282529283902083516080810185528154861680825293820154861692810192909252600281015485169382019390935260039092015490921692810192909252909550156117d5576060808301516001600160a01b039081166040808901919091528301511690860152875185908990600290811061178157611781614151565b602002602001018190525080886003815181106117a0576117a0614151565b602002602001018190525082886004815181106117bf576117bf614151565b60200260200101819052506114c5886005612f3b565b5f6117e68784606001516001613004565b80519091506001600160a01b031661184857611802895f612f3b565b6040518060400160405280601e81526020017f4c3a204e6f7420666f756e6420706f6f6c20666f7220746f6b656e496e3300008152509850985050505050505050612259565b808960028151811061185c5761185c614151565b6020026020010181905250896001600160a01b031681606001516001600160a01b0316036118ae5761188f896003612f3b565b60405180602001604052805f8152509850985050505050505050612259565b83604001516001600160a01b031681606001516001600160a01b0316036118f95783896003815181106118e3576118e3614151565b602002602001018190525061188f896004612f3b565b81604001516001600160a01b031681606001516001600160a01b03160361196357818960038151811061192e5761192e614151565b6020026020010181905250838960048151811061194d5761194d614151565b602002602001018190525061188f896005612f3b565b5f6119738884604001515f613004565b80519091506001600160a01b03166119d65761198f8a5f612f3b565b6040518060400160405280601f81526020017f4c3a204e6f7420666f756e6420706f6f6c20666f7220746f6b656e4f75743300815250995099505050505050505050612259565b80604001516001600160a01b031686606001516001600160a01b031603611a7f57808a600181518110611a0b57611a0b614151565b6020026020010181905250828a600281518110611a2a57611a2a614151565b6020026020010181905250848a600381518110611a4957611a49614151565b6020026020010181905250611a5f8a6004612f3b565b60405180602001604052805f815250995099505050505050505050612259565b80604001516001600160a01b031684606001516001600160a01b031603611b0857808a600281518110611ab457611ab4614151565b6020026020010181905250828a600381518110611ad357611ad3614151565b6020026020010181905250848a600481518110611af257611af2614151565b6020026020010181905250611a5f8a6005612f3b565b80604001516001600160a01b031682606001516001600160a01b031603611b9157808a600381518110611b3d57611b3d614151565b6020026020010181905250828a600481518110611b5c57611b5c614151565b6020026020010181905250848a600581518110611b7b57611b7b614151565b6020026020010181905250611a5f8a6006612f3b565b80604001516001600160a01b03168c6001600160a01b031603611c1557808a5f81518110611bc157611bc1614151565b6020026020010181905250828a600181518110611be057611be0614151565b6020026020010181905250848a600281518110611bff57611bff614151565b6020026020010181905250611a5f8a6003612f3b565b5f611c268984606001516001613004565b80519091506001600160a01b0316611c8a57611c428b5f612f3b565b6040518060400160405280601e81526020017f4c3a204e6f7420666f756e6420706f6f6c20666f7220746f6b656e496e3400008152509a509a50505050505050505050612259565b808b600381518110611c9e57611c9e614151565b60200260200101819052508b6001600160a01b031681606001516001600160a01b031603611cf257611cd18b6004612f3b565b60405180602001604052805f8152509a509a50505050505050505050612259565b85604001516001600160a01b031681606001516001600160a01b031603611d3d57858b600481518110611d2757611d27614151565b6020026020010181905250611cd18b6005612f3b565b83604001516001600160a01b031681606001516001600160a01b031603611da757838b600481518110611d7257611d72614151565b6020026020010181905250858b600581518110611d9157611d91614151565b6020026020010181905250611cd18b6006612f3b565b81604001516001600160a01b031681606001516001600160a01b031603611e3057818b600481518110611ddc57611ddc614151565b6020026020010181905250838b600581518110611dfb57611dfb614151565b6020026020010181905250858b600681518110611e1a57611e1a614151565b6020026020010181905250611cd18b6007612f3b565b5f611e408a84604001515f613004565b80519091506001600160a01b0316611ea557611e5c8c5f612f3b565b6040518060400160405280601f81526020017f4c3a204e6f7420666f756e6420706f6f6c20666f7220746f6b656e4f757434008152509b509b5050505050505050505050612259565b80604001516001600160a01b031688606001516001600160a01b031603611f6f57808c600181518110611eda57611eda614151565b6020026020010181905250828c600281518110611ef957611ef9614151565b6020026020010181905250848c600381518110611f1857611f18614151565b6020026020010181905250868c600481518110611f3757611f37614151565b6020026020010181905250611f4d8c6005612f3b565b60405180602001604052805f8152509b509b5050505050505050505050612259565b80604001516001600160a01b031686606001516001600160a01b03160361201757808c600281518110611fa457611fa4614151565b6020026020010181905250828c600381518110611fc357611fc3614151565b6020026020010181905250848c600481518110611fe257611fe2614151565b6020026020010181905250868c60058151811061200157612001614151565b6020026020010181905250611f4d8c6006612f3b565b80604001516001600160a01b031684606001516001600160a01b0316036120bf57808c60038151811061204c5761204c614151565b6020026020010181905250828c60048151811061206b5761206b614151565b6020026020010181905250848c60058151811061208a5761208a614151565b6020026020010181905250868c6006815181106120a9576120a9614151565b6020026020010181905250611f4d8c6007612f3b565b80604001516001600160a01b031682606001516001600160a01b03160361216757808c6004815181106120f4576120f4614151565b6020026020010181905250828c60058151811061211357612113614151565b6020026020010181905250848c60068151811061213257612132614151565b6020026020010181905250868c60078151811061215157612151614151565b6020026020010181905250611f4d8c6008612f3b565b80604001516001600160a01b03168e6001600160a01b03160361220a57808c5f8151811061219757612197614151565b6020026020010181905250828c6001815181106121b6576121b6614151565b6020026020010181905250848c6002815181106121d5576121d5614151565b6020026020010181905250868c6003815181106121f4576121f4614151565b6020026020010181905250611f4d8c6004612f3b565b6122148c5f612f3b565b6040518060400160405280601c81526020017f537761707065723a20737761702070617468206e6f7420666f756e64000000008152509b509b50505050505050505050505b9250929050565b60605f5160206144d15f395f51905f526122997fa3f85328863358c70a5d8558b355ddce3bfd90131b6ba971b451f8def7c6e703613320565b91505090565b60605f5160206144d15f395f51905f525f6122c65f5160206144f15f395f51905f52613320565b80519091505f6122d860038501613320565b8051909150825f5b8281101561232f576123178482815181106122fd576122fd614151565b60200260200101518860050161332c90919063ffffffff16565b61232757612324826142ec565b91505b6001016122e0565b5f826001600160401b0381111561234857612348613a9f565b604051908082528060200260200182016040528015612371578160200160208202803683370190505b5090505f91505b858210156123d75786828151811061239257612392614151565b60200260200101518183815181106123ac576123ac614151565b60200260200101906001600160a01b031690816001600160a01b031681525050816001019150612378565b5f5b8481101561246c576124108682815181106123f6576123f6614151565b60200260200101518a60050161332c90919063ffffffff16565b6124645785818151811061242657612426614151565b602002602001015182848151811061244057612440614151565b6001600160a01b0390921660209283029190910190910152612461836142ec565b92505b6001016123d9565b5098975050505050505050565b5f5f6124858484610dc1565b50511515949350505050565b5f5f61249d8585610dc1565b50905080515f036124b1575f9150506125fb565b5f83156124bf57508261252d565b856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124fb573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061251f9190614304565b61252a90600a614407565b90505b81515f5b818110156125f4575f84828151811061254c5761254c614151565b60209081029190910181015190810151815160408084015160608501519151633752511b60e11b81526001600160a01b03938416600482015290831660248201529082166044820152606481018890529293501690636ea4a23690608401602060405180830381865afa1580156125c5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125e99190614415565b935050600101612531565b5090925050505b9392505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156126465750825b90505f826001600160401b031660011480156126615750303b155b90508115801561266f575080155b1561268d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156126b757845460ff60401b1916600160401b1785555b6126c08661334d565b831561270657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b60605f5160206144d15f395f51905f526122995f5160206144f15f395f51905f52613320565b61273c612c61565b81515f5160206144d15f395f51905f52905f5b8181101561085b575f85828151811061276a5761276a614151565b6020908102919091018101516040808201516001600160a01b039081165f90815260018901855282812060608501518316825290945292205490925016158015906127b3575084155b156127d1576040516333e9449d60e21b815260040160405180910390fd5b604081810180516001600160a01b039081165f9081526001888101602081815286842060608901805187168652908252878520895181546001600160a01b0319908116918916919091178255838b01805183880180548416918b169190911790558951600280850180548516928c16929092179091558451600394850180548516918c16918217905589529585528a88208a518a16895290945298909520895181548a1690881617815591519382018054891694871694909417909355945190850180548716828616179055905193909101805490941692909116919091179091556128bc90612d18565b6128c98160600151612d18565b7fa99a974bf9b388be84d2af666c0e09a93de5d8cbecb06f32025cbcd1c14011b1816040516128f8919061402c565b60405180910390a15060010161274f565b612911612c61565b81515f5160206144d15f395f51905f52905f5b8181101561085b575f85828151811061293f5761293f614151565b6020908102919091018101516040808201516001600160a01b039081165f908152938890529220549092501615801590612977575084155b15612995576040516333e9449d60e21b815260040160405180910390fd5b604081810180516001600160a01b039081165f9081526020888152938120855181546001600160a01b031990811691851691909117825594860151600182018054871691851691909117905592516002840180548616828516179055606086015160039485018054909616931692909217909355612a1591870190612d04565b90507fea9169860adcad97f4b578ffd6a73fb02c6bf17502f0c4a724e9886009882eef8282604051612a48929190614250565b60405180910390a15050600101612924565b5f808215612a69575081612af4565b835f81518110612a7b57612a7b614151565b6020026020010151604001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ac2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ae69190614304565b612af190600a614407565b90505b83515f5b81811015612bbb575f868281518110612b1357612b13614151565b60209081029190910181015190810151815160408084015160608501519151633752511b60e11b81526001600160a01b03938416600482015290831660248201529082166044820152606481018890529293501690636ea4a23690608401602060405180830381865afa158015612b8c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612bb09190614415565b935050600101612af8565b5090949350505050565b5f5160206144d15f395f51905f525f80612bdf8787610dc1565b9150915081515f03612c0e578060405162461bcd60e51b8152600401612c05919061403a565b60405180910390fd5b6001600160a01b0387165f90815260028401602052604090205480861015612c4c57604051632671e51960e21b815260048101829052602401612c05565b612c57838787612d52565b5050505050505050565b612c69610c9f565b6040516336b87bd760e11b81523360048201526001600160a01b039190911690636d70f7ae90602401602060405180830381865afa158015612cad573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612cd1919061442c565b612cee57604051631f0853c160e21b815260040160405180910390fd5b565b5f6125fb836001600160a01b0384166134aa565b5f6125fb836001600160a01b03841661358d565b5f5160206144d15f395f51905f52612d3d5f5160206144f15f395f51905f528361332c565b612d4e57610dbc6005820183612d04565b5050565b82515f03612d7357604051630ef9926760e21b815260040160405180910390fd5b82515f5b81811015612ea3575f858281518110612d9257612d92614151565b60200260200101519050815f03612dcb57612dcb3382602001518784604001516001600160a01b03166135d9909392919063ffffffff16565b5f612dd7600185614281565b8314612e0c5786612de9846001614447565b81518110612df957612df9614151565b6020026020010151602001519050612e0f565b50335b602082015182516040808501516060860151915163023693a760e61b81526001600160a01b03938416600482015290831660248201529082166044820152838216606482015260848101889052911690638da4e9c09060a4015f604051808303815f87803b158015612e7f575f5ffd5b505af1158015612e91573d5f5f3e3d5ffd5b505060019094019350612d7792505050565b5083612eb0600183614281565b81518110612ec057612ec0614151565b6020026020010151606001516001600160a01b0316845f81518110612ee757612ee7614151565b6020026020010151604001516001600160a01b03167fea368a40e9570069bb8e6511d668293ad2e1f03b0d982431fd223de9f3b70ca685604051612f2d91815260200190565b60405180910390a350505050565b60605f826001600160401b03811115612f5657612f56613a9f565b604051908082528060200260200182016040528015612fa657816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f19909201910181612f745790505b5090505f5b83811015612ff257848181518110612fc557612fc5614151565b6020026020010151828281518110612fdf57612fdf614151565b6020908102919091010152600101612fab565b50612ffc81613639565b949350505050565b604080516080810182525f808252602082018190529181018290526060810191909152506001600160a01b038083165f81815260208681526040918290208251608081018452815486168152600182015486169281019290925260028101548516928201839052600301549093166060840152036125fb5781156131c15780606001516001600160a01b031681604001516001600160a01b0316036131bc575f816020015190506040518060400160405280600781526020016613595d185554d160ca1b81525080519060200120816001600160a01b0316638a7f53606040518163ffffffff1660e01b81526004015f60405180830381865afa15801561310d573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613134919081019061445a565b80519060200120036131ba5781516040516366cdc6a760e11b81526001600160a01b0391821660048201529082169063cd9b8d4e90602401602060405180830381865afa158015613187573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131ab919061448b565b6001600160a01b031660608301525b505b6125fb565b80606001516001600160a01b031681604001516001600160a01b0316036132fb575f816020015190506040518060400160405280600781526020016613595d185554d160ca1b81525080519060200120816001600160a01b0316638a7f53606040518163ffffffff1660e01b81526004015f60405180830381865afa15801561324c573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613273919081019061445a565b80519060200120036132f9578151604051638142e40760e01b81526001600160a01b03918216600482015290821690638142e40790602401602060405180830381865afa1580156132c6573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132ea919061448b565b6001600160a01b031660608301525b505b6060810180516040830180516001600160a01b03908116909352911690529392505050565b60605f6125fb8361381f565b6001600160a01b0381165f90815260018301602052604081205415156125fb565b613355613878565b6001600160a01b038116158015906133de57505f6001600160a01b0316816001600160a01b0316634783c35b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156133ae573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133d2919061448b565b6001600160a01b031614155b6133fb576040516371c42ac360e01b815260040160405180910390fd5b61342e61342960017faa116a42804728f23983458454b6eb9c6ddf3011db9f9addaf3cd7508d85b0d6614281565b829055565b6134604361345d60017f812a673dfca07956350df10f8a654925f561d7a0da09bdbe79e653939a14d9f1614281565b55565b604080516001600160a01b0383168152426020820152438183015290517f1a2dd071001ebf6e03174e3df5b305795a4ad5d41d8fdb9ba41dbbe2367134269181900360600190a150565b5f8181526001830160205260408120548015613584575f6134cc600183614281565b85549091505f906134df90600190614281565b905080821461353e575f865f0182815481106134fd576134fd614151565b905f5260205f200154905080875f01848154811061351d5761351d614151565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061354f5761354f6144a6565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610499565b5f915050610499565b5f8181526001830160205260408120546135d257508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610499565b505f610499565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526136339085906138c1565b50505050565b8051819060041161381a578160018151811061365757613657614151565b6020026020010151606001516001600160a01b0316825f8151811061367e5761367e614151565b6020026020010151604001516001600160a01b03161480156136ec5750816001815181106136ae576136ae614151565b6020026020010151604001516001600160a01b0316825f815181106136d5576136d5614151565b6020026020010151606001516001600160a01b0316145b80156137445750815f8151811061370557613705614151565b6020026020010151604001516001600160a01b03168260028151811061372d5761372d614151565b6020026020010151604001516001600160a01b0316145b1561381a57600282516137579190614281565b6001600160401b0381111561376e5761376e613a9f565b6040519080825280602002602001820160405280156137be57816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f1990920191018161378c5790505b50905060025b8251811015613818578281815181106137df576137df614151565b6020026020010151826002836137f59190614281565b8151811061380557613805614151565b60209081029190910101526001016137c4565b505b919050565b6060815f0180548060200260200160405190810160405280929190818152602001828054801561386c57602002820191905f5260205f20905b815481526020019060010190808311613858575b50505050509050919050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff16612cee57604051631afcd79f60e31b815260040160405180910390fd5b5f6138d56001600160a01b03841683613922565b905080515f141580156138f95750808060200190518101906138f7919061442c565b155b15610dbc57604051635274afe760e01b81526001600160a01b0384166004820152602401612c05565b60606125fb83835f845f5f856001600160a01b0316848660405161394691906144ba565b5f6040518083038185875af1925050503d805f8114613980576040519150601f19603f3d011682016040523d82523d5f602084013e613985565b606091505b509150915061399586838361399f565b9695505050505050565b6060826139af576131bc826139f6565b81511580156139c657506001600160a01b0384163b155b156139ef57604051639996b31560e01b81526001600160a01b0385166004820152602401612c05565b50806125fb565b805115613a065780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b50565b5f60208284031215613a32575f5ffd5b81356001600160e01b0319811681146125fb575f5ffd5b6001600160a01b0381168114613a1f575f5ffd5b803561381a81613a49565b5f5f60408385031215613a79575f5ffd5b8235613a8481613a49565b91506020830135613a9481613a49565b809150509250929050565b634e487b7160e01b5f52604160045260245ffd5b604051608081016001600160401b0381118282101715613ad557613ad5613a9f565b60405290565b604051601f8201601f191681016001600160401b0381118282101715613b0357613b03613a9f565b604052919050565b5f6001600160401b03821115613b2357613b23613a9f565b5060051b60200190565b5f6001600160401b03821115613b4557613b45613a9f565b50601f01601f191660200190565b8015158114613a1f575f5ffd5b803561381a81613b53565b5f5f60408385031215613b7c575f5ffd5b82356001600160401b03811115613b91575f5ffd5b8301601f81018513613ba1575f5ffd5b8035613bb4613baf82613b0b565b613adb565b8082825260208201915060208360051b850101925087831115613bd5575f5ffd5b602084015b83811015613cbe5780356001600160401b03811115613bf7575f5ffd5b85016080818b03601f19011215613c0c575f5ffd5b613c14613ab3565b6020820135613c2281613a49565b815260408201356001600160401b03811115613c3c575f5ffd5b82016020810190603f018c13613c50575f5ffd5b8035613c5e613baf82613b2d565b8181528d6020838501011115613c72575f5ffd5b816020840160208301375f60208383010152806020850152505050613c9960608301613a5d565b6040820152613caa60808301613a5d565b606082015284525060209283019201613bda565b509450613cd19250505060208401613b60565b90509250929050565b5f60208284031215613cea575f5ffd5b81356125fb81613a49565b5f82601f830112613d04575f5ffd5b8135613d12613baf82613b0b565b8082825260208201915060208360051b860101925085831115613d33575f5ffd5b602085015b83811015613d50578035835260209283019201613d38565b5095945050505050565b5f5f60408385031215613d6b575f5ffd5b82356001600160401b03811115613d80575f5ffd5b8301601f81018513613d90575f5ffd5b8035613d9e613baf82613b0b565b8082825260208201915060208360051b850101925087831115613dbf575f5ffd5b6020840193505b82841015613dea578335613dd981613a49565b825260209384019390910190613dc6565b945050505060208301356001600160401b03811115613e07575f5ffd5b613e1385828601613cf5565b9150509250929050565b5f82601f830112613e2c575f5ffd5b8135613e3a613baf82613b0b565b8082825260208201915060208360071b860101925085831115613e5b575f5ffd5b602085015b83811015613d505760808188031215613e77575f5ffd5b613e7f613ab3565b8135613e8a81613a49565b81526020820135613e9a81613a49565b60208201526040820135613ead81613a49565b60408201526060820135613ec081613a49565b60608201528352602090920191608001613e60565b5f5f5f60608486031215613ee7575f5ffd5b83356001600160401b03811115613efc575f5ffd5b613f0886828701613e1d565b9660208601359650604090950135949350505050565b80516001600160a01b03908116835260208083015182169084015260408083015182169084015260609182015116910152565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b604080825283519082018190525f9060208501906060840190835b81811015613fc357613fad838551613f1e565b6020939093019260809290920191600101613f9a565b505083810360208501526139958186613f51565b5f8151808452602084019350602083015f5b828110156140105781516001600160a01b0316865260209586019590910190600101613fe9565b5093949350505050565b602081525f6125fb6020830184613fd7565b608081016104998284613f1e565b602081525f6125fb6020830184613f51565b5f5f5f6060848603121561405e575f5ffd5b833561406981613a49565b9250602084013561407981613a49565b929592945050506040919091013590565b5f5f6040838503121561409b575f5ffd5b82356001600160401b038111156140b0575f5ffd5b6140bc85828601613e1d565b9250506020830135613a9481613b53565b5f5f604083850312156140de575f5ffd5b82356001600160401b038111156140f3575f5ffd5b6140ff85828601613e1d565b95602094909401359450505050565b5f5f5f5f60808587031215614121575f5ffd5b843561412c81613a49565b9350602085013561413c81613a49565b93969395505050506040820135916060013590565b634e487b7160e01b5f52603260045260245ffd5b5f82601f830112614174575f5ffd5b8151614182613baf82613b2d565b818152846020838601011115614196575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f602082840312156141c2575f5ffd5b81516001600160401b038111156141d7575f5ffd5b8201604081850312156141e8575f5ffd5b604080519081016001600160401b038111828210171561420a5761420a613a9f565b60405281516001600160401b03811115614222575f5ffd5b61422e86828501614165565b8252506020820151915061424182613a49565b60208101919091529392505050565b60a0810161425e8285613f1e565b82151560808301529392505050565b634e487b7160e01b5f52601160045260245ffd5b818103818111156104995761049961426d565b604081525f6142a66040830185613fd7565b82810360208401528084518083526020830191506020860192505f5b818110156142e05783518352602093840193909201916001016142c2565b50909695505050505050565b5f600182016142fd576142fd61426d565b5060010190565b5f60208284031215614314575f5ffd5b815160ff811681146125fb575f5ffd5b6001815b600184111561435f578085048111156143435761434361426d565b600184161561435157908102905b60019390931c928002614328565b935093915050565b5f8261437557506001610499565b8161438157505f610499565b816001811461439757600281146143a1576143bd565b6001915050610499565b60ff8411156143b2576143b261426d565b50506001821b610499565b5060208310610133831016604e8410600b84101617156143e0575081810a610499565b6143ec5f198484614324565b805f19048211156143ff576143ff61426d565b029392505050565b5f6125fb60ff841683614367565b5f60208284031215614425575f5ffd5b5051919050565b5f6020828403121561443c575f5ffd5b81516125fb81613b53565b808201808211156104995761049961426d565b5f6020828403121561446a575f5ffd5b81516001600160401b0381111561447f575f5ffd5b612ffc84828501614165565b5f6020828403121561449b575f5ffd5b81516125fb81613a49565b634e487b7160e01b5f52603160045260245ffd5b5f82518060208501845e5f92019182525091905056fea3f85328863358c70a5d8558b355ddce3bfd90131b6ba971b451f8def7c6e700a3f85328863358c70a5d8558b355ddce3bfd90131b6ba971b451f8def7c6e705537761707065723a204e6f7420666f756e6420706f6f6c20666f7220746f6b656e4f7574537761707065723a204e6f7420666f756e6420706f6f6c20666f7220746f6b656e496ea26469706673582212206b9b2d7afc98bc73e0098639668f7cd153dd0ccad8a43702d1931c60382cb91a64736f6c634300081c0033
Deployed Bytecode
0x608060405234801561000f575f5ffd5b506004361061016d575f3560e01c806391a6ba43116100d9578063c6a9b3d411610093578063d87de3181161006e578063d87de3181461040c578063f97e42161461041f578063fe02915614610432578063ffa1ad7414610445575f5ffd5b8063c6a9b3d4146103aa578063c7b07d72146103b2578063c86ec2bf146103c5575f5ffd5b806391a6ba4314610281578063936725ec14610338578063951cbc6314610369578063a9dd14d61461037c578063ba6d313d1461038f578063c4d66de814610397575f5ffd5b80634bde38c81161012a5780634bde38c8146101fd578063570fa5bc1461021d5780635b504baf146102305780636e8811021461024357806371a973051461026457806376d708d714610279575f5ffd5b806301ffc9a714610171578063371d6587146101995780633a79f2c8146101ae5780633a85fcab146101c15780633b7d0946146101d45780634593144c146101e7575b5f5ffd5b61018461017f366004613a22565b610469565b60405190151581526020015b60405180910390f35b6101ac6101a7366004613a68565b61049f565b005b6101ac6101bc366004613b6b565b6105a4565b6101ac6101cf366004613b6b565b610862565b6101ac6101e2366004613cda565b610b9e565b6101ef610c67565b604051908152602001610190565b610205610c9f565b6040516001600160a01b039091168152602001610190565b6101ac61022b366004613d5a565b610cce565b6101ac61023e366004613ed5565b610db1565b610256610251366004613a68565b610dc1565b604051610190929190613f7f565b61026c612260565b604051610190919061401a565b61026c61229f565b61032b61028f366004613a68565b60408051608080820183525f808352602080840182905283850182905260609384018290526001600160a01b0396871682527fa3f85328863358c70a5d8558b355ddce3bfd90131b6ba971b451f8def7c6e7018152848220958716825294855283902083519182018452805486168252600181015486169482019490945260028401548516928101929092526003909201549092169082015290565b604051610190919061402c565b61035c60405180604001604052806005815260200164312e302e3160d81b81525081565b604051610190919061403a565b610184610377366004613a68565b612479565b6101ef61038a36600461404c565b612491565b6101ef600881565b6101ac6103a5366004613cda565b612602565b61026c61270e565b6101ac6103c036600461408a565b612734565b6101ef6103d3366004613cda565b6001600160a01b03165f9081527fa3f85328863358c70a5d8558b355ddce3bfd90131b6ba971b451f8def7c6e702602052604090205490565b6101ac61041a36600461408a565b612909565b6101ef61042d3660046140cd565b612a5a565b6101ac61044036600461410e565b612bc5565b61035c604051806040016040528060058152602001640312e332e360dc1b81525081565b5f6001600160e01b03198216630f1ec81f60e41b148061049957506301ffc9a760e01b6001600160e01b03198316145b92915050565b6104a7612c61565b6001600160a01b038281165f9081527fa3f85328863358c70a5d8558b355ddce3bfd90131b6ba971b451f8def7c6e7016020908152604080832093851683529290522080546001600160a01b0319908116825560018201805482169055600282018054821690556003909101805490911690555f5160206144d15f395f51905f5261053f5f5160206144f15f395f51905f5284612cf0565b61055c5760405163ad5679e160e01b815260040160405180910390fd5b604080516001600160a01b038086168252841660208201527f0244dbcda3b6229e4548013cf904734fe7f8e256767bb56343a05ae08e0eae2d910160405180910390a1505050565b6105ac612c61565b81515f5160206144d15f395f51905f52905f5b8181101561085b57604080516080810182525f8082526020820181905291810182905260608101919091528582815181106105fc576105fc614151565b6020908102919091010151516001600160a01b03168152855186908390811061062757610627614151565b60200260200101516040015181604001906001600160a01b031690816001600160a01b03168152505085828151811061066257610662614151565b60209081029190910101516060908101516001600160a01b031690820152610688610c9f565b6001600160a01b0316634254af1c8784815181106106a8576106a8614151565b602002602001015160200151805190602001206040518263ffffffff1660e01b81526004016106d991815260200190565b5f60405180830381865afa1580156106f3573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261071a91908101906141b2565b6020908101516001600160a01b031690820181905261074c576040516313ffb03f60e11b815260040160405180910390fd5b6040808201516001600160a01b039081165f908152602087905291909120541615801590610778575084155b15610796576040516333e9449d60e21b815260040160405180910390fd5b604081810180516001600160a01b039081165f9081526020888152938120855181546001600160a01b03199081169185169190911782559486015160018201805487169185169190911790559251600284018054861682851617905560608601516003948501805490961693169290921790935561081691870190612d04565b90507fea9169860adcad97f4b578ffd6a73fb02c6bf17502f0c4a724e9886009882eef8282604051610849929190614250565b60405180910390a150506001016105bf565b5050505050565b61086a612c61565b81515f5160206144d15f395f51905f52905f5b8181101561085b57604080516080810182525f8082526020820181905291810182905260608101919091528582815181106108ba576108ba614151565b6020908102919091010151516001600160a01b0316815285518690839081106108e5576108e5614151565b60200260200101516040015181604001906001600160a01b031690816001600160a01b03168152505085828151811061092057610920614151565b60209081029190910101516060908101516001600160a01b031690820152610946610c9f565b6001600160a01b0316634254af1c87848151811061096657610966614151565b602002602001015160200151805190602001206040518263ffffffff1660e01b815260040161099791815260200190565b5f60405180830381865afa1580156109b1573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526109d891908101906141b2565b6020908101516001600160a01b0316908201819052610a0a576040516313ffb03f60e11b815260040160405180910390fd5b6040808201516001600160a01b039081165f908152600187016020908152838220606086015184168352905291909120541615801590610a48575084155b15610a66576040516333e9449d60e21b815260040160405180910390fd5b604081810180516001600160a01b039081165f9081526001888101602081815286842060608901805187168652908252878520895181546001600160a01b0319908116918916919091178255838b01805183880180548416918b169190911790558951600280850180548516928c16929092179091558451600394850180548516918c16918217905589529585528a88208a518a16895290945298909520895181548a169088161781559151938201805489169487169490941790935594519085018054871682861617905590519390910180549094169290911691909117909155610b5190612d18565b610b5e8160600151612d18565b7fa99a974bf9b388be84d2af666c0e09a93de5d8cbecb06f32025cbcd1c14011b181604051610b8d919061402c565b60405180910390a15060010161087d565b610ba6612c61565b6001600160a01b0381165f9081525f5160206144d15f395f51905f526020819052604090912080546001600160a01b031990811682556001820180548216905560028201805482169055600390910180549091169055610c267fa3f85328863358c70a5d8558b355ddce3bfd90131b6ba971b451f8def7c6e70383612cf0565b506040516001600160a01b03831681527f4106dfdaa577573db51c0ca93f766dbedfa0758faa2e7f5bcdb7c142be803c3f9060200160405180910390a15050565b5f610c9a610c9660017f812a673dfca07956350df10f8a654925f561d7a0da09bdbe79e653939a14d9f1614281565b5490565b905090565b5f610c9a610c9660017faa116a42804728f23983458454b6eb9c6ddf3011db9f9addaf3cd7508d85b0d6614281565b610cd6612c61565b815181515f5160206144d15f395f51905f529190808214610d0a57604051630ef9926760e21b815260040160405180910390fd5b5f5b82811015610d7057848181518110610d2657610d26614151565b6020026020010151846002015f888481518110610d4557610d45614151565b6020908102919091018101516001600160a01b031682528101919091526040015f2055600101610d0c565b507f30e6061dda0fa9ccf5d58c384f3bacd9468b361b792ee40087e70fe248c5adaf8585604051610da2929190614294565b60405180910390a15050505050565b610dbc838383612d52565b505050565b604080516008808252610120820190925260609182915f5160206144d15f395f51905f5291602082015b604080516080810182525f8082526020808301829052928201819052606082015282525f19909201910181610deb5750506001600160a01b038681165f9081526001848101602090815260408084208a86168552825292839020835160808101855281548616808252938201548616928101929092526002810154851693820193909352600390920154909216606082015291945015610ee0576001600160a01b038087166040830152851660608201528351819085905f90610eb057610eb0614151565b6020026020010181905250610ec6846001612f3b565b60405180602001604052805f815250935093505050612259565b5f610eed83886001613004565b80519091506001600160a01b0316610f2e57610f09855f612f3b565b6040518060600160405280602381526020016145356023913994509450505050612259565b80855f81518110610f4157610f41614151565b6020026020010181905250856001600160a01b031681606001516001600160a01b031603610f8f57610f74856001612f3b565b60405180602001604052805f81525094509450505050612259565b6060808201516001600160a01b039081165f9081526001808701602090815260408084208c861685528252928390208351608081018552815486168082529382015486169281019290925260028101548516938201939093526003909201549092169281019290925290925015611049576060808201516001600160a01b039081166040850152871690830152845182908690600190811061103357611033614151565b6020026020010181905250610f74856002612f3b565b5f61105584885f613004565b80519091506001600160a01b031661109757611071865f612f3b565b604051806060016040528060248152602001614511602491399550955050505050612259565b80604001516001600160a01b0316886001600160a01b0316036110f95780865f815181106110c7576110c7614151565b60200260200101819052506110dd866001612f3b565b60405180602001604052805f8152509550955050505050612259565b80604001516001600160a01b031682606001516001600160a01b03160361114457808660018151811061112e5761112e614151565b60200260200101819052506110dd866002612f3b565b6060808301516001600160a01b039081165f90815260018088016020908152604080842087820151861685528252928390208351608081018552815486168082529382015486169281019290925260028101548516938201939093526003909201549092169281019290925290935015611226576060808301516001600160a01b03908116604080870191909152830151169084015285518390879060019081106111f1576111f1614151565b6020026020010181905250808660028151811061121057611210614151565b60200260200101819052506110dd866003612f3b565b5f6112378584606001516001613004565b80519091506001600160a01b031661129757611253875f612f3b565b6040518060400160405280601e81526020017f4c3a204e6f7420666f756e6420706f6f6c20666f7220746f6b656e496e320000815250965096505050505050612259565b80876001815181106112ab576112ab614151565b6020026020010181905250876001600160a01b031681606001516001600160a01b0316036112fb576112de876002612f3b565b60405180602001604052805f815250965096505050505050612259565b81604001516001600160a01b031681606001516001600160a01b03160361134657818760028151811061133057611330614151565b60200260200101819052506112de876003612f3b565b6060808201516001600160a01b039081165f9081526001808901602090815260408084208e8616855282529283902083516080810185528154861680825293820154861692810192909252600281015485169382019390935260039092015490921692810192909252909450156113ea576060808201516001600160a01b039081166040870152891690850152865184908890600290811061133057611330614151565b5f6113fa8684604001515f613004565b80519091506001600160a01b031661145b57611416885f612f3b565b6040518060400160405280601f81526020017f4c3a204e6f7420666f756e6420706f6f6c20666f7220746f6b656e4f7574320081525097509750505050505050612259565b80604001516001600160a01b031684606001516001600160a01b0316036114e357808860018151811061149057611490614151565b602002602001018190525082886002815181106114af576114af614151565b60200260200101819052506114c5886003612f3b565b60405180602001604052805f81525097509750505050505050612259565b80604001516001600160a01b031682606001516001600160a01b03160361154d57808860028151811061151857611518614151565b6020026020010181905250828860038151811061153757611537614151565b60200260200101819052506114c5886004612f3b565b6001600160a01b03808b165f90815260018089016020908152604080842086820151861685528252928390208351608081018552815486168082529382015486169281019290925260028101548516938201939093526003909201549092166060820152955015611608576001600160a01b03808b166040808801919091528201511660608601528751859089905f906115e9576115e9614151565b6020026020010181905250808860018151811061149057611490614151565b6060808501516001600160a01b039081165f9081526001808a0160209081526040808420878201518616855282529283902083516080810185528154861680825293820154861692810192909252600281015485169382019390935260039092015490921692810192909252909550156116d4576060808501516001600160a01b03908116604080890191909152830151169086015287518590899060019081106116b5576116b5614151565b6020026020010181905250808860028151811061151857611518614151565b6060808301516001600160a01b039081165f9081526001808a0160209081526040808420878201518616855282529283902083516080810185528154861680825293820154861692810192909252600281015485169382019390935260039092015490921692810192909252909550156117d5576060808301516001600160a01b039081166040808901919091528301511690860152875185908990600290811061178157611781614151565b602002602001018190525080886003815181106117a0576117a0614151565b602002602001018190525082886004815181106117bf576117bf614151565b60200260200101819052506114c5886005612f3b565b5f6117e68784606001516001613004565b80519091506001600160a01b031661184857611802895f612f3b565b6040518060400160405280601e81526020017f4c3a204e6f7420666f756e6420706f6f6c20666f7220746f6b656e496e3300008152509850985050505050505050612259565b808960028151811061185c5761185c614151565b6020026020010181905250896001600160a01b031681606001516001600160a01b0316036118ae5761188f896003612f3b565b60405180602001604052805f8152509850985050505050505050612259565b83604001516001600160a01b031681606001516001600160a01b0316036118f95783896003815181106118e3576118e3614151565b602002602001018190525061188f896004612f3b565b81604001516001600160a01b031681606001516001600160a01b03160361196357818960038151811061192e5761192e614151565b6020026020010181905250838960048151811061194d5761194d614151565b602002602001018190525061188f896005612f3b565b5f6119738884604001515f613004565b80519091506001600160a01b03166119d65761198f8a5f612f3b565b6040518060400160405280601f81526020017f4c3a204e6f7420666f756e6420706f6f6c20666f7220746f6b656e4f75743300815250995099505050505050505050612259565b80604001516001600160a01b031686606001516001600160a01b031603611a7f57808a600181518110611a0b57611a0b614151565b6020026020010181905250828a600281518110611a2a57611a2a614151565b6020026020010181905250848a600381518110611a4957611a49614151565b6020026020010181905250611a5f8a6004612f3b565b60405180602001604052805f815250995099505050505050505050612259565b80604001516001600160a01b031684606001516001600160a01b031603611b0857808a600281518110611ab457611ab4614151565b6020026020010181905250828a600381518110611ad357611ad3614151565b6020026020010181905250848a600481518110611af257611af2614151565b6020026020010181905250611a5f8a6005612f3b565b80604001516001600160a01b031682606001516001600160a01b031603611b9157808a600381518110611b3d57611b3d614151565b6020026020010181905250828a600481518110611b5c57611b5c614151565b6020026020010181905250848a600581518110611b7b57611b7b614151565b6020026020010181905250611a5f8a6006612f3b565b80604001516001600160a01b03168c6001600160a01b031603611c1557808a5f81518110611bc157611bc1614151565b6020026020010181905250828a600181518110611be057611be0614151565b6020026020010181905250848a600281518110611bff57611bff614151565b6020026020010181905250611a5f8a6003612f3b565b5f611c268984606001516001613004565b80519091506001600160a01b0316611c8a57611c428b5f612f3b565b6040518060400160405280601e81526020017f4c3a204e6f7420666f756e6420706f6f6c20666f7220746f6b656e496e3400008152509a509a50505050505050505050612259565b808b600381518110611c9e57611c9e614151565b60200260200101819052508b6001600160a01b031681606001516001600160a01b031603611cf257611cd18b6004612f3b565b60405180602001604052805f8152509a509a50505050505050505050612259565b85604001516001600160a01b031681606001516001600160a01b031603611d3d57858b600481518110611d2757611d27614151565b6020026020010181905250611cd18b6005612f3b565b83604001516001600160a01b031681606001516001600160a01b031603611da757838b600481518110611d7257611d72614151565b6020026020010181905250858b600581518110611d9157611d91614151565b6020026020010181905250611cd18b6006612f3b565b81604001516001600160a01b031681606001516001600160a01b031603611e3057818b600481518110611ddc57611ddc614151565b6020026020010181905250838b600581518110611dfb57611dfb614151565b6020026020010181905250858b600681518110611e1a57611e1a614151565b6020026020010181905250611cd18b6007612f3b565b5f611e408a84604001515f613004565b80519091506001600160a01b0316611ea557611e5c8c5f612f3b565b6040518060400160405280601f81526020017f4c3a204e6f7420666f756e6420706f6f6c20666f7220746f6b656e4f757434008152509b509b5050505050505050505050612259565b80604001516001600160a01b031688606001516001600160a01b031603611f6f57808c600181518110611eda57611eda614151565b6020026020010181905250828c600281518110611ef957611ef9614151565b6020026020010181905250848c600381518110611f1857611f18614151565b6020026020010181905250868c600481518110611f3757611f37614151565b6020026020010181905250611f4d8c6005612f3b565b60405180602001604052805f8152509b509b5050505050505050505050612259565b80604001516001600160a01b031686606001516001600160a01b03160361201757808c600281518110611fa457611fa4614151565b6020026020010181905250828c600381518110611fc357611fc3614151565b6020026020010181905250848c600481518110611fe257611fe2614151565b6020026020010181905250868c60058151811061200157612001614151565b6020026020010181905250611f4d8c6006612f3b565b80604001516001600160a01b031684606001516001600160a01b0316036120bf57808c60038151811061204c5761204c614151565b6020026020010181905250828c60048151811061206b5761206b614151565b6020026020010181905250848c60058151811061208a5761208a614151565b6020026020010181905250868c6006815181106120a9576120a9614151565b6020026020010181905250611f4d8c6007612f3b565b80604001516001600160a01b031682606001516001600160a01b03160361216757808c6004815181106120f4576120f4614151565b6020026020010181905250828c60058151811061211357612113614151565b6020026020010181905250848c60068151811061213257612132614151565b6020026020010181905250868c60078151811061215157612151614151565b6020026020010181905250611f4d8c6008612f3b565b80604001516001600160a01b03168e6001600160a01b03160361220a57808c5f8151811061219757612197614151565b6020026020010181905250828c6001815181106121b6576121b6614151565b6020026020010181905250848c6002815181106121d5576121d5614151565b6020026020010181905250868c6003815181106121f4576121f4614151565b6020026020010181905250611f4d8c6004612f3b565b6122148c5f612f3b565b6040518060400160405280601c81526020017f537761707065723a20737761702070617468206e6f7420666f756e64000000008152509b509b50505050505050505050505b9250929050565b60605f5160206144d15f395f51905f526122997fa3f85328863358c70a5d8558b355ddce3bfd90131b6ba971b451f8def7c6e703613320565b91505090565b60605f5160206144d15f395f51905f525f6122c65f5160206144f15f395f51905f52613320565b80519091505f6122d860038501613320565b8051909150825f5b8281101561232f576123178482815181106122fd576122fd614151565b60200260200101518860050161332c90919063ffffffff16565b61232757612324826142ec565b91505b6001016122e0565b5f826001600160401b0381111561234857612348613a9f565b604051908082528060200260200182016040528015612371578160200160208202803683370190505b5090505f91505b858210156123d75786828151811061239257612392614151565b60200260200101518183815181106123ac576123ac614151565b60200260200101906001600160a01b031690816001600160a01b031681525050816001019150612378565b5f5b8481101561246c576124108682815181106123f6576123f6614151565b60200260200101518a60050161332c90919063ffffffff16565b6124645785818151811061242657612426614151565b602002602001015182848151811061244057612440614151565b6001600160a01b0390921660209283029190910190910152612461836142ec565b92505b6001016123d9565b5098975050505050505050565b5f5f6124858484610dc1565b50511515949350505050565b5f5f61249d8585610dc1565b50905080515f036124b1575f9150506125fb565b5f83156124bf57508261252d565b856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124fb573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061251f9190614304565b61252a90600a614407565b90505b81515f5b818110156125f4575f84828151811061254c5761254c614151565b60209081029190910181015190810151815160408084015160608501519151633752511b60e11b81526001600160a01b03938416600482015290831660248201529082166044820152606481018890529293501690636ea4a23690608401602060405180830381865afa1580156125c5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125e99190614415565b935050600101612531565b5090925050505b9392505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156126465750825b90505f826001600160401b031660011480156126615750303b155b90508115801561266f575080155b1561268d5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156126b757845460ff60401b1916600160401b1785555b6126c08661334d565b831561270657845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b60605f5160206144d15f395f51905f526122995f5160206144f15f395f51905f52613320565b61273c612c61565b81515f5160206144d15f395f51905f52905f5b8181101561085b575f85828151811061276a5761276a614151565b6020908102919091018101516040808201516001600160a01b039081165f90815260018901855282812060608501518316825290945292205490925016158015906127b3575084155b156127d1576040516333e9449d60e21b815260040160405180910390fd5b604081810180516001600160a01b039081165f9081526001888101602081815286842060608901805187168652908252878520895181546001600160a01b0319908116918916919091178255838b01805183880180548416918b169190911790558951600280850180548516928c16929092179091558451600394850180548516918c16918217905589529585528a88208a518a16895290945298909520895181548a1690881617815591519382018054891694871694909417909355945190850180548716828616179055905193909101805490941692909116919091179091556128bc90612d18565b6128c98160600151612d18565b7fa99a974bf9b388be84d2af666c0e09a93de5d8cbecb06f32025cbcd1c14011b1816040516128f8919061402c565b60405180910390a15060010161274f565b612911612c61565b81515f5160206144d15f395f51905f52905f5b8181101561085b575f85828151811061293f5761293f614151565b6020908102919091018101516040808201516001600160a01b039081165f908152938890529220549092501615801590612977575084155b15612995576040516333e9449d60e21b815260040160405180910390fd5b604081810180516001600160a01b039081165f9081526020888152938120855181546001600160a01b031990811691851691909117825594860151600182018054871691851691909117905592516002840180548616828516179055606086015160039485018054909616931692909217909355612a1591870190612d04565b90507fea9169860adcad97f4b578ffd6a73fb02c6bf17502f0c4a724e9886009882eef8282604051612a48929190614250565b60405180910390a15050600101612924565b5f808215612a69575081612af4565b835f81518110612a7b57612a7b614151565b6020026020010151604001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ac2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ae69190614304565b612af190600a614407565b90505b83515f5b81811015612bbb575f868281518110612b1357612b13614151565b60209081029190910181015190810151815160408084015160608501519151633752511b60e11b81526001600160a01b03938416600482015290831660248201529082166044820152606481018890529293501690636ea4a23690608401602060405180830381865afa158015612b8c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612bb09190614415565b935050600101612af8565b5090949350505050565b5f5160206144d15f395f51905f525f80612bdf8787610dc1565b9150915081515f03612c0e578060405162461bcd60e51b8152600401612c05919061403a565b60405180910390fd5b6001600160a01b0387165f90815260028401602052604090205480861015612c4c57604051632671e51960e21b815260048101829052602401612c05565b612c57838787612d52565b5050505050505050565b612c69610c9f565b6040516336b87bd760e11b81523360048201526001600160a01b039190911690636d70f7ae90602401602060405180830381865afa158015612cad573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612cd1919061442c565b612cee57604051631f0853c160e21b815260040160405180910390fd5b565b5f6125fb836001600160a01b0384166134aa565b5f6125fb836001600160a01b03841661358d565b5f5160206144d15f395f51905f52612d3d5f5160206144f15f395f51905f528361332c565b612d4e57610dbc6005820183612d04565b5050565b82515f03612d7357604051630ef9926760e21b815260040160405180910390fd5b82515f5b81811015612ea3575f858281518110612d9257612d92614151565b60200260200101519050815f03612dcb57612dcb3382602001518784604001516001600160a01b03166135d9909392919063ffffffff16565b5f612dd7600185614281565b8314612e0c5786612de9846001614447565b81518110612df957612df9614151565b6020026020010151602001519050612e0f565b50335b602082015182516040808501516060860151915163023693a760e61b81526001600160a01b03938416600482015290831660248201529082166044820152838216606482015260848101889052911690638da4e9c09060a4015f604051808303815f87803b158015612e7f575f5ffd5b505af1158015612e91573d5f5f3e3d5ffd5b505060019094019350612d7792505050565b5083612eb0600183614281565b81518110612ec057612ec0614151565b6020026020010151606001516001600160a01b0316845f81518110612ee757612ee7614151565b6020026020010151604001516001600160a01b03167fea368a40e9570069bb8e6511d668293ad2e1f03b0d982431fd223de9f3b70ca685604051612f2d91815260200190565b60405180910390a350505050565b60605f826001600160401b03811115612f5657612f56613a9f565b604051908082528060200260200182016040528015612fa657816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f19909201910181612f745790505b5090505f5b83811015612ff257848181518110612fc557612fc5614151565b6020026020010151828281518110612fdf57612fdf614151565b6020908102919091010152600101612fab565b50612ffc81613639565b949350505050565b604080516080810182525f808252602082018190529181018290526060810191909152506001600160a01b038083165f81815260208681526040918290208251608081018452815486168152600182015486169281019290925260028101548516928201839052600301549093166060840152036125fb5781156131c15780606001516001600160a01b031681604001516001600160a01b0316036131bc575f816020015190506040518060400160405280600781526020016613595d185554d160ca1b81525080519060200120816001600160a01b0316638a7f53606040518163ffffffff1660e01b81526004015f60405180830381865afa15801561310d573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613134919081019061445a565b80519060200120036131ba5781516040516366cdc6a760e11b81526001600160a01b0391821660048201529082169063cd9b8d4e90602401602060405180830381865afa158015613187573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131ab919061448b565b6001600160a01b031660608301525b505b6125fb565b80606001516001600160a01b031681604001516001600160a01b0316036132fb575f816020015190506040518060400160405280600781526020016613595d185554d160ca1b81525080519060200120816001600160a01b0316638a7f53606040518163ffffffff1660e01b81526004015f60405180830381865afa15801561324c573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613273919081019061445a565b80519060200120036132f9578151604051638142e40760e01b81526001600160a01b03918216600482015290821690638142e40790602401602060405180830381865afa1580156132c6573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132ea919061448b565b6001600160a01b031660608301525b505b6060810180516040830180516001600160a01b03908116909352911690529392505050565b60605f6125fb8361381f565b6001600160a01b0381165f90815260018301602052604081205415156125fb565b613355613878565b6001600160a01b038116158015906133de57505f6001600160a01b0316816001600160a01b0316634783c35b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156133ae573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133d2919061448b565b6001600160a01b031614155b6133fb576040516371c42ac360e01b815260040160405180910390fd5b61342e61342960017faa116a42804728f23983458454b6eb9c6ddf3011db9f9addaf3cd7508d85b0d6614281565b829055565b6134604361345d60017f812a673dfca07956350df10f8a654925f561d7a0da09bdbe79e653939a14d9f1614281565b55565b604080516001600160a01b0383168152426020820152438183015290517f1a2dd071001ebf6e03174e3df5b305795a4ad5d41d8fdb9ba41dbbe2367134269181900360600190a150565b5f8181526001830160205260408120548015613584575f6134cc600183614281565b85549091505f906134df90600190614281565b905080821461353e575f865f0182815481106134fd576134fd614151565b905f5260205f200154905080875f01848154811061351d5761351d614151565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061354f5761354f6144a6565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610499565b5f915050610499565b5f8181526001830160205260408120546135d257508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610499565b505f610499565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526136339085906138c1565b50505050565b8051819060041161381a578160018151811061365757613657614151565b6020026020010151606001516001600160a01b0316825f8151811061367e5761367e614151565b6020026020010151604001516001600160a01b03161480156136ec5750816001815181106136ae576136ae614151565b6020026020010151604001516001600160a01b0316825f815181106136d5576136d5614151565b6020026020010151606001516001600160a01b0316145b80156137445750815f8151811061370557613705614151565b6020026020010151604001516001600160a01b03168260028151811061372d5761372d614151565b6020026020010151604001516001600160a01b0316145b1561381a57600282516137579190614281565b6001600160401b0381111561376e5761376e613a9f565b6040519080825280602002602001820160405280156137be57816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f1990920191018161378c5790505b50905060025b8251811015613818578281815181106137df576137df614151565b6020026020010151826002836137f59190614281565b8151811061380557613805614151565b60209081029190910101526001016137c4565b505b919050565b6060815f0180548060200260200160405190810160405280929190818152602001828054801561386c57602002820191905f5260205f20905b815481526020019060010190808311613858575b50505050509050919050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff16612cee57604051631afcd79f60e31b815260040160405180910390fd5b5f6138d56001600160a01b03841683613922565b905080515f141580156138f95750808060200190518101906138f7919061442c565b155b15610dbc57604051635274afe760e01b81526001600160a01b0384166004820152602401612c05565b60606125fb83835f845f5f856001600160a01b0316848660405161394691906144ba565b5f6040518083038185875af1925050503d805f8114613980576040519150601f19603f3d011682016040523d82523d5f602084013e613985565b606091505b509150915061399586838361399f565b9695505050505050565b6060826139af576131bc826139f6565b81511580156139c657506001600160a01b0384163b155b156139ef57604051639996b31560e01b81526001600160a01b0385166004820152602401612c05565b50806125fb565b805115613a065780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b50565b5f60208284031215613a32575f5ffd5b81356001600160e01b0319811681146125fb575f5ffd5b6001600160a01b0381168114613a1f575f5ffd5b803561381a81613a49565b5f5f60408385031215613a79575f5ffd5b8235613a8481613a49565b91506020830135613a9481613a49565b809150509250929050565b634e487b7160e01b5f52604160045260245ffd5b604051608081016001600160401b0381118282101715613ad557613ad5613a9f565b60405290565b604051601f8201601f191681016001600160401b0381118282101715613b0357613b03613a9f565b604052919050565b5f6001600160401b03821115613b2357613b23613a9f565b5060051b60200190565b5f6001600160401b03821115613b4557613b45613a9f565b50601f01601f191660200190565b8015158114613a1f575f5ffd5b803561381a81613b53565b5f5f60408385031215613b7c575f5ffd5b82356001600160401b03811115613b91575f5ffd5b8301601f81018513613ba1575f5ffd5b8035613bb4613baf82613b0b565b613adb565b8082825260208201915060208360051b850101925087831115613bd5575f5ffd5b602084015b83811015613cbe5780356001600160401b03811115613bf7575f5ffd5b85016080818b03601f19011215613c0c575f5ffd5b613c14613ab3565b6020820135613c2281613a49565b815260408201356001600160401b03811115613c3c575f5ffd5b82016020810190603f018c13613c50575f5ffd5b8035613c5e613baf82613b2d565b8181528d6020838501011115613c72575f5ffd5b816020840160208301375f60208383010152806020850152505050613c9960608301613a5d565b6040820152613caa60808301613a5d565b606082015284525060209283019201613bda565b509450613cd19250505060208401613b60565b90509250929050565b5f60208284031215613cea575f5ffd5b81356125fb81613a49565b5f82601f830112613d04575f5ffd5b8135613d12613baf82613b0b565b8082825260208201915060208360051b860101925085831115613d33575f5ffd5b602085015b83811015613d50578035835260209283019201613d38565b5095945050505050565b5f5f60408385031215613d6b575f5ffd5b82356001600160401b03811115613d80575f5ffd5b8301601f81018513613d90575f5ffd5b8035613d9e613baf82613b0b565b8082825260208201915060208360051b850101925087831115613dbf575f5ffd5b6020840193505b82841015613dea578335613dd981613a49565b825260209384019390910190613dc6565b945050505060208301356001600160401b03811115613e07575f5ffd5b613e1385828601613cf5565b9150509250929050565b5f82601f830112613e2c575f5ffd5b8135613e3a613baf82613b0b565b8082825260208201915060208360071b860101925085831115613e5b575f5ffd5b602085015b83811015613d505760808188031215613e77575f5ffd5b613e7f613ab3565b8135613e8a81613a49565b81526020820135613e9a81613a49565b60208201526040820135613ead81613a49565b60408201526060820135613ec081613a49565b60608201528352602090920191608001613e60565b5f5f5f60608486031215613ee7575f5ffd5b83356001600160401b03811115613efc575f5ffd5b613f0886828701613e1d565b9660208601359650604090950135949350505050565b80516001600160a01b03908116835260208083015182169084015260408083015182169084015260609182015116910152565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b604080825283519082018190525f9060208501906060840190835b81811015613fc357613fad838551613f1e565b6020939093019260809290920191600101613f9a565b505083810360208501526139958186613f51565b5f8151808452602084019350602083015f5b828110156140105781516001600160a01b0316865260209586019590910190600101613fe9565b5093949350505050565b602081525f6125fb6020830184613fd7565b608081016104998284613f1e565b602081525f6125fb6020830184613f51565b5f5f5f6060848603121561405e575f5ffd5b833561406981613a49565b9250602084013561407981613a49565b929592945050506040919091013590565b5f5f6040838503121561409b575f5ffd5b82356001600160401b038111156140b0575f5ffd5b6140bc85828601613e1d565b9250506020830135613a9481613b53565b5f5f604083850312156140de575f5ffd5b82356001600160401b038111156140f3575f5ffd5b6140ff85828601613e1d565b95602094909401359450505050565b5f5f5f5f60808587031215614121575f5ffd5b843561412c81613a49565b9350602085013561413c81613a49565b93969395505050506040820135916060013590565b634e487b7160e01b5f52603260045260245ffd5b5f82601f830112614174575f5ffd5b8151614182613baf82613b2d565b818152846020838601011115614196575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f602082840312156141c2575f5ffd5b81516001600160401b038111156141d7575f5ffd5b8201604081850312156141e8575f5ffd5b604080519081016001600160401b038111828210171561420a5761420a613a9f565b60405281516001600160401b03811115614222575f5ffd5b61422e86828501614165565b8252506020820151915061424182613a49565b60208101919091529392505050565b60a0810161425e8285613f1e565b82151560808301529392505050565b634e487b7160e01b5f52601160045260245ffd5b818103818111156104995761049961426d565b604081525f6142a66040830185613fd7565b82810360208401528084518083526020830191506020860192505f5b818110156142e05783518352602093840193909201916001016142c2565b50909695505050505050565b5f600182016142fd576142fd61426d565b5060010190565b5f60208284031215614314575f5ffd5b815160ff811681146125fb575f5ffd5b6001815b600184111561435f578085048111156143435761434361426d565b600184161561435157908102905b60019390931c928002614328565b935093915050565b5f8261437557506001610499565b8161438157505f610499565b816001811461439757600281146143a1576143bd565b6001915050610499565b60ff8411156143b2576143b261426d565b50506001821b610499565b5060208310610133831016604e8410600b84101617156143e0575081810a610499565b6143ec5f198484614324565b805f19048211156143ff576143ff61426d565b029392505050565b5f6125fb60ff841683614367565b5f60208284031215614425575f5ffd5b5051919050565b5f6020828403121561443c575f5ffd5b81516125fb81613b53565b808201808211156104995761049961426d565b5f6020828403121561446a575f5ffd5b81516001600160401b0381111561447f575f5ffd5b612ffc84828501614165565b5f6020828403121561449b575f5ffd5b81516125fb81613a49565b634e487b7160e01b5f52603160045260245ffd5b5f82518060208501845e5f92019182525091905056fea3f85328863358c70a5d8558b355ddce3bfd90131b6ba971b451f8def7c6e700a3f85328863358c70a5d8558b355ddce3bfd90131b6ba971b451f8def7c6e705537761707065723a204e6f7420666f756e6420706f6f6c20666f7220746f6b656e4f7574537761707065723a204e6f7420666f756e6420706f6f6c20666f7220746f6b656e496ea26469706673582212206b9b2d7afc98bc73e0098639668f7cd153dd0ccad8a43702d1931c60382cb91a64736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in S
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.