Transactions
Token Transfers
Tokens
Internal Transactions
Coin Balance History
Code
Read Contract
Write Contract
- Contract name:
- UniswapV2Router02
- Optimization enabled
- true
- Compiler version
- v0.7.6+commit.7338295f
- Optimization runs
- 1000
- EVM Version
- default
- Verified at
- 2021-04-13T22:26:27.648385Z
Constructor Arguments
00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd
Arg [0] (address) : 0x17854c8d5a41d5a89b275386e24b2f38fd0afbdd
Arg [1] (address) : 0x6b3bd0478df0ec4984b168db0e12a539cc0c83cd
Contract source code
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6; interface IUniswapV2ERC20 { event Approval( address indexed owner, address indexed spender, uint256 value ); event Transfer(address indexed from, address indexed to, uint256 value); function name() external pure returns (string memory); function symbol() external pure returns (string memory); function decimals() external pure returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address owner) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 value) external returns (bool); function transfer(address to, uint256 value) external returns (bool); function transferFrom( address from, address to, uint256 value ) external returns (bool); function DOMAIN_SEPARATOR() external view returns (bytes32); function PERMIT_TYPEHASH() external pure returns (bytes32); function nonces(address owner) external view returns (uint256); function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; } interface IUniswapV2Router01 { function factory() external view returns (address); function WETH() external view returns (address); function addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns ( uint256 amountA, uint256 amountB, uint256 liquidity ); function addLiquidityETH( address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external payable returns ( uint256 amountToken, uint256 amountETH, uint256 liquidity ); function removeLiquidity( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns (uint256 amountA, uint256 amountB); function removeLiquidityETH( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external returns (uint256 amountToken, uint256 amountETH); function removeLiquidityWithPermit( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountA, uint256 amountB); function removeLiquidityETHWithPermit( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountToken, uint256 amountETH); function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapTokensForExactTokens( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapExactETHForTokens( uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external payable returns (uint256[] memory amounts); function swapTokensForExactETH( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapExactTokensForETH( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapETHForExactTokens( uint256 amountOut, address[] calldata path, address to, uint256 deadline ) external payable returns (uint256[] memory amounts); function quote( uint256 amountA, uint256 reserveA, uint256 reserveB ) external pure returns (uint256 amountB); function getAmountOut( uint256 amountIn, uint256 reserveIn, uint256 reserveOut ) external pure returns (uint256 amountOut); function getAmountIn( uint256 amountOut, uint256 reserveIn, uint256 reserveOut ) external pure returns (uint256 amountIn); function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts); function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts); } // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math) library SafeMath { function add(uint256 x, uint256 y) internal pure returns (uint256 z) { require((z = x + y) >= x, "ds-math-add-overflow"); } function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { require((z = x - y) <= x, "ds-math-sub-underflow"); } function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow"); } } interface IWETH { function deposit() external payable; function transfer(address to, uint256 value) external returns (bool); function withdraw(uint256) external; } interface IERC20 { event Approval( address indexed owner, address indexed spender, uint256 value ); event Transfer(address indexed from, address indexed to, uint256 value); function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address owner) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 value) external returns (bool); function transfer(address to, uint256 value) external returns (bool); function transferFrom( address from, address to, uint256 value ) external returns (bool); } interface IUniswapV2Pair is IUniswapV2ERC20 { event Mint(address indexed sender, uint256 amount0, uint256 amount1); event Burn( address indexed sender, uint256 amount0, uint256 amount1, address indexed to ); event Swap( address indexed sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to ); event Sync(uint112 reserve0, uint112 reserve1); function MINIMUM_LIQUIDITY() external pure returns (uint256); function factory() external view returns (address); function token0() external view returns (address); function token1() external view returns (address); function getReserves() external view returns ( uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast ); function price0CumulativeLast() external view returns (uint256); function price1CumulativeLast() external view returns (uint256); function kLast() external view returns (uint256); function mint(address to) external returns (uint256 liquidity); function burn(address to) external returns (uint256 amount0, uint256 amount1); function swap( uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data ) external; function skim(address to) external; function sync() external; function initialize(address, address) external; } library UniswapV2Library { using SafeMath for uint256; // returns sorted token addresses, used to handle return values from pairs sorted in this order function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) { require(tokenA != tokenB, "UniswapV2Library: IDENTICAL_ADDRESSES"); (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); require(token0 != address(0), "UniswapV2Library: ZERO_ADDRESS"); } // calculates the CREATE2 address for a pair without making any external calls function pairFor( address factory, address tokenA, address tokenB ) internal pure returns (address pair) { (address token0, address token1) = sortTokens(tokenA, tokenB); pair = address( uint256( keccak256( abi.encodePacked( hex"ff", factory, keccak256(abi.encodePacked(token0, token1)), hex"930f72af52e9f7553fd568961dff48b9d73ba993c0ddc3f637f6af07a34e97bd" // init code hash ) ) ) ); } // fetches and sorts the reserves for a pair function getReserves( address factory, address tokenA, address tokenB ) internal view returns (uint256 reserveA, uint256 reserveB) { (address token0, ) = sortTokens(tokenA, tokenB); (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves(); (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0); } // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset function quote( uint256 amountA, uint256 reserveA, uint256 reserveB ) internal pure returns (uint256 amountB) { require(amountA > 0, "UniswapV2Library: INSUFFICIENT_AMOUNT"); require( reserveA > 0 && reserveB > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY" ); amountB = amountA.mul(reserveB) / reserveA; } // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset function getAmountOut( uint256 amountIn, uint256 reserveIn, uint256 reserveOut ) internal pure returns (uint256 amountOut) { require(amountIn > 0, "UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT"); require( reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY" ); uint256 amountInWithFee = amountIn.mul(997); uint256 numerator = amountInWithFee.mul(reserveOut); uint256 denominator = reserveIn.mul(1000).add(amountInWithFee); amountOut = numerator / denominator; } // given an output amount of an asset and pair reserves, returns a required input amount of the other asset function getAmountIn( uint256 amountOut, uint256 reserveIn, uint256 reserveOut ) internal pure returns (uint256 amountIn) { require(amountOut > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT"); require( reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY" ); uint256 numerator = reserveIn.mul(amountOut).mul(1000); uint256 denominator = reserveOut.sub(amountOut).mul(997); amountIn = (numerator / denominator).add(1); } // performs chained getAmountOut calculations on any number of pairs function getAmountsOut( address factory, uint256 amountIn, address[] memory path ) internal view returns (uint256[] memory amounts) { require(path.length >= 2, "UniswapV2Library: INVALID_PATH"); amounts = new uint256[](path.length); amounts[0] = amountIn; for (uint256 i; i < path.length - 1; i++) { (uint256 reserveIn, uint256 reserveOut) = getReserves(factory, path[i], path[i + 1]); amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut); } } // performs chained getAmountIn calculations on any number of pairs function getAmountsIn( address factory, uint256 amountOut, address[] memory path ) internal view returns (uint256[] memory amounts) { require(path.length >= 2, "UniswapV2Library: INVALID_PATH"); amounts = new uint256[](path.length); amounts[amounts.length - 1] = amountOut; for (uint256 i = path.length - 1; i > 0; i--) { (uint256 reserveIn, uint256 reserveOut) = getReserves(factory, path[i - 1], path[i]); amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut); } } } // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false library TransferHelper { function safeApprove( address token, address to, uint256 value ) internal { // bytes4(keccak256(bytes('approve(address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value)); require( success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper::safeApprove: approve failed" ); } function safeTransfer( address token, address to, uint256 value ) internal { // bytes4(keccak256(bytes('transfer(address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); require( success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper::safeTransfer: transfer failed" ); } function safeTransferFrom( address token, address from, address to, uint256 value ) internal { // bytes4(keccak256(bytes('transferFrom(address,address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); require( success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper::transferFrom: transferFrom failed" ); } function safeTransferETH(address to, uint256 value) internal { (bool success, ) = to.call{value: value}(new bytes(0)); require( success, "TransferHelper::safeTransferETH: ETH transfer failed" ); } } interface IUniswapV2Factory { event PairCreated( address indexed token0, address indexed token1, address pair, uint256 ); function feeTo() external view returns (address); function feeToSetter() external view returns (address); function getPair(address tokenA, address tokenB) external view returns (address pair); function allPairs(uint256) external view returns (address pair); function allPairsLength() external view returns (uint256); function createPair(address tokenA, address tokenB) external returns (address pair); function setFeeTo(address) external; function setFeeToSetter(address) external; } interface IUniswapV2Router02 is IUniswapV2Router01 { function removeLiquidityETHSupportingFeeOnTransferTokens( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external returns (uint256 amountETH); function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountETH); function swapExactTokensForTokensSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external; function swapExactETHForTokensSupportingFeeOnTransferTokens( uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external payable; function swapExactTokensForETHSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external; } contract UniswapV2Router02 is IUniswapV2Router02 { using SafeMath for uint256; address public immutable override factory; address public immutable override WETH; modifier ensure(uint256 deadline) { require(deadline >= block.timestamp, "UniswapV2Router: EXPIRED"); _; } constructor(address _factory, address _WETH) { factory = _factory; WETH = _WETH; } receive() external payable { assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract } // **** ADD LIQUIDITY **** function _addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin ) internal virtual returns (uint256 amountA, uint256 amountB) { // create the pair if it doesn't exist yet if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) { IUniswapV2Factory(factory).createPair(tokenA, tokenB); } (uint256 reserveA, uint256 reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB); if (reserveA == 0 && reserveB == 0) { (amountA, amountB) = (amountADesired, amountBDesired); } else { uint256 amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB); if (amountBOptimal <= amountBDesired) { require( amountBOptimal >= amountBMin, "UniswapV2Router: INSUFFICIENT_B_AMOUNT" ); (amountA, amountB) = (amountADesired, amountBOptimal); } else { uint256 amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA); assert(amountAOptimal <= amountADesired); require( amountAOptimal >= amountAMin, "UniswapV2Router: INSUFFICIENT_A_AMOUNT" ); (amountA, amountB) = (amountAOptimal, amountBDesired); } } } function addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external virtual override ensure(deadline) returns ( uint256 amountA, uint256 amountB, uint256 liquidity ) { (amountA, amountB) = _addLiquidity( tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin ); address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB); TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA); TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB); liquidity = IUniswapV2Pair(pair).mint(to); } function addLiquidityETH( address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external payable virtual override ensure(deadline) returns ( uint256 amountToken, uint256 amountETH, uint256 liquidity ) { (amountToken, amountETH) = _addLiquidity( token, WETH, amountTokenDesired, msg.value, amountTokenMin, amountETHMin ); address pair = UniswapV2Library.pairFor(factory, token, WETH); TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken); IWETH(WETH).deposit{value: amountETH}(); assert(IWETH(WETH).transfer(pair, amountETH)); liquidity = IUniswapV2Pair(pair).mint(to); // refund dust eth, if any if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH); } // **** REMOVE LIQUIDITY **** function removeLiquidity( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) public virtual override ensure(deadline) returns (uint256 amountA, uint256 amountB) { address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB); IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair (uint256 amount0, uint256 amount1) = IUniswapV2Pair(pair).burn(to); (address token0, ) = UniswapV2Library.sortTokens(tokenA, tokenB); (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0); require( amountA >= amountAMin, "UniswapV2Router: INSUFFICIENT_A_AMOUNT" ); require( amountB >= amountBMin, "UniswapV2Router: INSUFFICIENT_B_AMOUNT" ); } function removeLiquidityETH( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) public virtual override ensure(deadline) returns (uint256 amountToken, uint256 amountETH) { (amountToken, amountETH) = removeLiquidity( token, WETH, liquidity, amountTokenMin, amountETHMin, address(this), deadline ); TransferHelper.safeTransfer(token, to, amountToken); IWETH(WETH).withdraw(amountETH); TransferHelper.safeTransferETH(to, amountETH); } function removeLiquidityWithPermit( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external virtual override returns (uint256 amountA, uint256 amountB) { address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB); uint256 value = approveMax ? uint256(-1) : liquidity; IUniswapV2Pair(pair).permit( msg.sender, address(this), value, deadline, v, r, s ); (amountA, amountB) = removeLiquidity( tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline ); } function removeLiquidityETHWithPermit( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external virtual override returns (uint256 amountToken, uint256 amountETH) { address pair = UniswapV2Library.pairFor(factory, token, WETH); uint256 value = approveMax ? uint256(-1) : liquidity; IUniswapV2Pair(pair).permit( msg.sender, address(this), value, deadline, v, r, s ); (amountToken, amountETH) = removeLiquidityETH( token, liquidity, amountTokenMin, amountETHMin, to, deadline ); } // **** REMOVE LIQUIDITY (supporting fee-on-transfer tokens) **** function removeLiquidityETHSupportingFeeOnTransferTokens( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) public virtual override ensure(deadline) returns (uint256 amountETH) { (, amountETH) = removeLiquidity( token, WETH, liquidity, amountTokenMin, amountETHMin, address(this), deadline ); TransferHelper.safeTransfer( token, to, IERC20(token).balanceOf(address(this)) ); IWETH(WETH).withdraw(amountETH); TransferHelper.safeTransferETH(to, amountETH); } function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external virtual override returns (uint256 amountETH) { address pair = UniswapV2Library.pairFor(factory, token, WETH); uint256 value = approveMax ? uint256(-1) : liquidity; IUniswapV2Pair(pair).permit( msg.sender, address(this), value, deadline, v, r, s ); amountETH = removeLiquidityETHSupportingFeeOnTransferTokens( token, liquidity, amountTokenMin, amountETHMin, to, deadline ); } // **** SWAP **** // requires the initial amount to have already been sent to the first pair function _swap( uint256[] memory amounts, address[] memory path, address _to ) internal virtual { for (uint256 i; i < path.length - 1; i++) { (address input, address output) = (path[i], path[i + 1]); (address token0, ) = UniswapV2Library.sortTokens(input, output); uint256 amountOut = amounts[i + 1]; (uint256 amount0Out, uint256 amount1Out) = input == token0 ? (uint256(0), amountOut) : (amountOut, uint256(0)); address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to; IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)) .swap(amount0Out, amount1Out, to, new bytes(0)); } } function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external virtual override ensure(deadline) returns (uint256[] memory amounts) { amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path); require( amounts[amounts.length - 1] >= amountOutMin, "UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT" ); TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, to); } function swapTokensForExactTokens( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external virtual override ensure(deadline) returns (uint256[] memory amounts) { amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path); require( amounts[0] <= amountInMax, "UniswapV2Router: EXCESSIVE_INPUT_AMOUNT" ); TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, to); } function swapExactETHForTokens( uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external payable virtual override ensure(deadline) returns (uint256[] memory amounts) { require(path[0] == WETH, "UniswapV2Router: INVALID_PATH"); amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path); require( amounts[amounts.length - 1] >= amountOutMin, "UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT" ); IWETH(WETH).deposit{value: amounts[0]}(); assert( IWETH(WETH).transfer( UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ) ); _swap(amounts, path, to); } function swapTokensForExactETH( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external virtual override ensure(deadline) returns (uint256[] memory amounts) { require(path[path.length - 1] == WETH, "UniswapV2Router: INVALID_PATH"); amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path); require( amounts[0] <= amountInMax, "UniswapV2Router: EXCESSIVE_INPUT_AMOUNT" ); TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, address(this)); IWETH(WETH).withdraw(amounts[amounts.length - 1]); TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]); } function swapExactTokensForETH( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external virtual override ensure(deadline) returns (uint256[] memory amounts) { require(path[path.length - 1] == WETH, "UniswapV2Router: INVALID_PATH"); amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path); require( amounts[amounts.length - 1] >= amountOutMin, "UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT" ); TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ); _swap(amounts, path, address(this)); IWETH(WETH).withdraw(amounts[amounts.length - 1]); TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]); } function swapETHForExactTokens( uint256 amountOut, address[] calldata path, address to, uint256 deadline ) external payable virtual override ensure(deadline) returns (uint256[] memory amounts) { require(path[0] == WETH, "UniswapV2Router: INVALID_PATH"); amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path); require( amounts[0] <= msg.value, "UniswapV2Router: EXCESSIVE_INPUT_AMOUNT" ); IWETH(WETH).deposit{value: amounts[0]}(); assert( IWETH(WETH).transfer( UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] ) ); _swap(amounts, path, to); // refund dust eth, if any if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]); } // **** SWAP (supporting fee-on-transfer tokens) **** // requires the initial amount to have already been sent to the first pair function _swapSupportingFeeOnTransferTokens( address[] memory path, address _to ) internal virtual { for (uint256 i; i < path.length - 1; i++) { (address input, address output) = (path[i], path[i + 1]); (address token0, ) = UniswapV2Library.sortTokens(input, output); IUniswapV2Pair pair = IUniswapV2Pair( UniswapV2Library.pairFor(factory, input, output) ); uint256 amountInput; uint256 amountOutput; { // scope to avoid stack too deep errors (uint256 reserve0, uint256 reserve1, ) = pair.getReserves(); (uint256 reserveInput, uint256 reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0); amountInput = IERC20(input).balanceOf(address(pair)).sub( reserveInput ); amountOutput = UniswapV2Library.getAmountOut( amountInput, reserveInput, reserveOutput ); } (uint256 amount0Out, uint256 amount1Out) = input == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0)); address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to; pair.swap(amount0Out, amount1Out, to, new bytes(0)); } } function swapExactTokensForTokensSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external virtual override ensure(deadline) { TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn ); uint256 balanceBefore = IERC20(path[path.length - 1]).balanceOf(to); _swapSupportingFeeOnTransferTokens(path, to); require( IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin, "UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT" ); } function swapExactETHForTokensSupportingFeeOnTransferTokens( uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external payable virtual override ensure(deadline) { require(path[0] == WETH, "UniswapV2Router: INVALID_PATH"); uint256 amountIn = msg.value; IWETH(WETH).deposit{value: amountIn}(); assert( IWETH(WETH).transfer( UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn ) ); uint256 balanceBefore = IERC20(path[path.length - 1]).balanceOf(to); _swapSupportingFeeOnTransferTokens(path, to); require( IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin, "UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT" ); } function swapExactTokensForETHSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external virtual override ensure(deadline) { require(path[path.length - 1] == WETH, "UniswapV2Router: INVALID_PATH"); TransferHelper.safeTransferFrom( path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn ); _swapSupportingFeeOnTransferTokens(path, address(this)); uint256 amountOut = IERC20(WETH).balanceOf(address(this)); require( amountOut >= amountOutMin, "UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT" ); IWETH(WETH).withdraw(amountOut); TransferHelper.safeTransferETH(to, amountOut); } // **** LIBRARY FUNCTIONS **** function quote( uint256 amountA, uint256 reserveA, uint256 reserveB ) public pure virtual override returns (uint256 amountB) { return UniswapV2Library.quote(amountA, reserveA, reserveB); } function getAmountOut( uint256 amountIn, uint256 reserveIn, uint256 reserveOut ) public pure virtual override returns (uint256 amountOut) { return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut); } function getAmountIn( uint256 amountOut, uint256 reserveIn, uint256 reserveOut ) public pure virtual override returns (uint256 amountIn) { return UniswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut); } function getAmountsOut(uint256 amountIn, address[] memory path) public view virtual override returns (uint256[] memory amounts) { return UniswapV2Library.getAmountsOut(factory, amountIn, path); } function getAmountsIn(uint256 amountOut, address[] memory path) public view virtual override returns (uint256[] memory amounts) { return UniswapV2Library.getAmountsIn(factory, amountOut, path); } }
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_factory","internalType":"address"},{"type":"address","name":"_WETH","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"WETH","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountA","internalType":"uint256"},{"type":"uint256","name":"amountB","internalType":"uint256"},{"type":"uint256","name":"liquidity","internalType":"uint256"}],"name":"addLiquidity","inputs":[{"type":"address","name":"tokenA","internalType":"address"},{"type":"address","name":"tokenB","internalType":"address"},{"type":"uint256","name":"amountADesired","internalType":"uint256"},{"type":"uint256","name":"amountBDesired","internalType":"uint256"},{"type":"uint256","name":"amountAMin","internalType":"uint256"},{"type":"uint256","name":"amountBMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"amountToken","internalType":"uint256"},{"type":"uint256","name":"amountETH","internalType":"uint256"},{"type":"uint256","name":"liquidity","internalType":"uint256"}],"name":"addLiquidityETH","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amountTokenDesired","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountETHMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"factory","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"}],"name":"getAmountIn","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"uint256","name":"reserveIn","internalType":"uint256"},{"type":"uint256","name":"reserveOut","internalType":"uint256"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"getAmountOut","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"reserveIn","internalType":"uint256"},{"type":"uint256","name":"reserveOut","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"getAmountsIn","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"getAmountsOut","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"amountB","internalType":"uint256"}],"name":"quote","inputs":[{"type":"uint256","name":"amountA","internalType":"uint256"},{"type":"uint256","name":"reserveA","internalType":"uint256"},{"type":"uint256","name":"reserveB","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountA","internalType":"uint256"},{"type":"uint256","name":"amountB","internalType":"uint256"}],"name":"removeLiquidity","inputs":[{"type":"address","name":"tokenA","internalType":"address"},{"type":"address","name":"tokenB","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountAMin","internalType":"uint256"},{"type":"uint256","name":"amountBMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountToken","internalType":"uint256"},{"type":"uint256","name":"amountETH","internalType":"uint256"}],"name":"removeLiquidityETH","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountETHMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountETH","internalType":"uint256"}],"name":"removeLiquidityETHSupportingFeeOnTransferTokens","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountETHMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountToken","internalType":"uint256"},{"type":"uint256","name":"amountETH","internalType":"uint256"}],"name":"removeLiquidityETHWithPermit","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountETHMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"bool","name":"approveMax","internalType":"bool"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountETH","internalType":"uint256"}],"name":"removeLiquidityETHWithPermitSupportingFeeOnTransferTokens","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountETHMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"bool","name":"approveMax","internalType":"bool"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountA","internalType":"uint256"},{"type":"uint256","name":"amountB","internalType":"uint256"}],"name":"removeLiquidityWithPermit","inputs":[{"type":"address","name":"tokenA","internalType":"address"},{"type":"address","name":"tokenB","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountAMin","internalType":"uint256"},{"type":"uint256","name":"amountBMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"bool","name":"approveMax","internalType":"bool"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapETHForExactTokens","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapExactETHForTokens","inputs":[{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"swapExactETHForTokensSupportingFeeOnTransferTokens","inputs":[{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapExactTokensForETH","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"swapExactTokensForETHSupportingFeeOnTransferTokens","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapExactTokensForTokens","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"swapExactTokensForTokensSupportingFeeOnTransferTokens","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapTokensForExactETH","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"uint256","name":"amountInMax","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapTokensForExactTokens","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"uint256","name":"amountInMax","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"receive","stateMutability":"payable"}]
Deployed ByteCode
0x60806040526004361061018f5760003560e01c80638803dbee116100d6578063c45a01551161007f578063e8e3370011610059578063e8e3370014610ba1578063f305d71914610c21578063fb3bdb4114610c67576101c8565b8063c45a015514610a62578063d06ca61f14610a77578063ded9382a14610b2e576101c8565b8063af2979eb116100b0578063af2979eb1461092c578063b6f9de951461097f578063baa2abde14610a05576101c8565b80638803dbee1461082d578063ad5c4648146108c5578063ad615dec146108f6576101c8565b80634a25d94a11610138578063791ac94711610112578063791ac947146106d95780637ff36ab51461077157806385f8c259146107f7576101c8565b80634a25d94a146105365780635b0d5984146105ce5780635c11d79514610641576101c8565b80631f00ca74116101695780631f00ca74146103695780632195995c1461042057806338ed17391461049e576101c8565b806302751cec146101cd578063054d50d41461023957806318cbafe514610281576101c8565b366101c857336001600160a01b037f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd16146101c657fe5b005b600080fd5b3480156101d957600080fd5b50610220600480360360c08110156101f057600080fd5b506001600160a01b0381358116916020810135916040820135916060810135916080820135169060a00135610ced565b6040805192835260208301919091528051918290030190f35b34801561024557600080fd5b5061026f6004803603606081101561025c57600080fd5b5080359060208101359060400135610e07565b60408051918252519081900360200190f35b34801561028d57600080fd5b50610319600480360360a08110156102a457600080fd5b8135916020810135918101906060810160408201356401000000008111156102cb57600080fd5b8201836020820111156102dd57600080fd5b803590602001918460208302840111640100000000831117156102ff57600080fd5b91935091506001600160a01b038135169060200135610e1c565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561035557818101518382015260200161033d565b505050509050019250505060405180910390f35b34801561037557600080fd5b506103196004803603604081101561038c57600080fd5b813591908101906040810160208201356401000000008111156103ae57600080fd5b8201836020820111156103c057600080fd5b803590602001918460208302840111640100000000831117156103e257600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061115b945050505050565b34801561042c57600080fd5b50610220600480360361016081101561044457600080fd5b506001600160a01b038135811691602081013582169160408201359160608101359160808201359160a08101359091169060c08101359060e081013515159060ff6101008201351690610120810135906101400135611191565b3480156104aa57600080fd5b50610319600480360360a08110156104c157600080fd5b8135916020810135918101906060810160408201356401000000008111156104e857600080fd5b8201836020820111156104fa57600080fd5b8035906020019184602083028401116401000000008311171561051c57600080fd5b91935091506001600160a01b03813516906020013561128b565b34801561054257600080fd5b50610319600480360360a081101561055957600080fd5b81359160208101359181019060608101604082013564010000000081111561058057600080fd5b82018360208201111561059257600080fd5b803590602001918460208302840111640100000000831117156105b457600080fd5b91935091506001600160a01b0381351690602001356113d6565b3480156105da57600080fd5b5061026f60048036036101408110156105f257600080fd5b506001600160a01b0381358116916020810135916040820135916060810135916080820135169060a08101359060c081013515159060ff60e08201351690610100810135906101200135611574565b34801561064d57600080fd5b506101c6600480360360a081101561066457600080fd5b81359160208101359181019060608101604082013564010000000081111561068b57600080fd5b82018360208201111561069d57600080fd5b803590602001918460208302840111640100000000831117156106bf57600080fd5b91935091506001600160a01b038135169060200135611682565b3480156106e557600080fd5b506101c6600480360360a08110156106fc57600080fd5b81359160208101359181019060608101604082013564010000000081111561072357600080fd5b82018360208201111561073557600080fd5b8035906020019184602083028401116401000000008311171561075757600080fd5b91935091506001600160a01b0381351690602001356118ff565b6103196004803603608081101561078757600080fd5b813591908101906040810160208201356401000000008111156107a957600080fd5b8201836020820111156107bb57600080fd5b803590602001918460208302840111640100000000831117156107dd57600080fd5b91935091506001600160a01b038135169060200135611b9a565b34801561080357600080fd5b5061026f6004803603606081101561081a57600080fd5b5080359060208101359060400135611ef6565b34801561083957600080fd5b50610319600480360360a081101561085057600080fd5b81359160208101359181019060608101604082013564010000000081111561087757600080fd5b82018360208201111561088957600080fd5b803590602001918460208302840111640100000000831117156108ab57600080fd5b91935091506001600160a01b038135169060200135611f03565b3480156108d157600080fd5b506108da611ffc565b604080516001600160a01b039092168252519081900360200190f35b34801561090257600080fd5b5061026f6004803603606081101561091957600080fd5b5080359060208101359060400135612020565b34801561093857600080fd5b5061026f600480360360c081101561094f57600080fd5b506001600160a01b0381358116916020810135916040820135916060810135916080820135169060a0013561202d565b6101c66004803603608081101561099557600080fd5b813591908101906040810160208201356401000000008111156109b757600080fd5b8201836020820111156109c957600080fd5b803590602001918460208302840111640100000000831117156109eb57600080fd5b91935091506001600160a01b0381351690602001356121b3565b348015610a1157600080fd5b50610220600480360360e0811015610a2857600080fd5b506001600160a01b038135811691602081013582169160408201359160608101359160808201359160a08101359091169060c00135612536565b348015610a6e57600080fd5b506108da612793565b348015610a8357600080fd5b5061031960048036036040811015610a9a57600080fd5b81359190810190604081016020820135640100000000811115610abc57600080fd5b820183602082011115610ace57600080fd5b80359060200191846020830284011164010000000083111715610af057600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506127b7945050505050565b348015610b3a57600080fd5b506102206004803603610140811015610b5257600080fd5b506001600160a01b0381358116916020810135916040820135916060810135916080820135169060a08101359060c081013515159060ff60e082013516906101008101359061012001356127e4565b348015610bad57600080fd5b50610c036004803603610100811015610bc557600080fd5b506001600160a01b038135811691602081013582169160408201359160608101359160808201359160a08101359160c0820135169060e001356128f8565b60408051938452602084019290925282820152519081900360600190f35b610c03600480360360c0811015610c3757600080fd5b506001600160a01b0381358116916020810135916040820135916060810135916080820135169060a00135612a2b565b61031960048036036080811015610c7d57600080fd5b81359190810190604081016020820135640100000000811115610c9f57600080fd5b820183602082011115610cb157600080fd5b80359060200191846020830284011164010000000083111715610cd357600080fd5b91935091506001600160a01b038135169060200135612cbe565b6000808242811015610d34576040805162461bcd60e51b81526020600482015260186024820152600080516020614697833981519152604482015290519081900360640190fd5b610d63897f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd8a8a8a308a612536565b9093509150610d73898685613049565b7f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6001600160a01b0316632e1a7d4d836040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015610dd957600080fd5b505af1158015610ded573d6000803e3d6000fd5b50505050610dfb85836131ca565b50965096945050505050565b6000610e148484846132c2565b949350505050565b60608142811015610e62576040805162461bcd60e51b81526020600482015260186024820152600080516020614697833981519152604482015290519081900360640190fd5b6001600160a01b037f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd1686866000198101818110610e9c57fe5b905060200201356001600160a01b03166001600160a01b031614610f07576040805162461bcd60e51b815260206004820152601d60248201527f556e69737761705632526f757465723a20494e56414c49445f50415448000000604482015290519081900360640190fd5b610f657f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8988888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061339a92505050565b91508682600184510381518110610f7857fe5b60200260200101511015610fbd5760405162461bcd60e51b815260040180806020018281038252602b815260200180614614602b913960400191505060405180910390fd5b61105b86866000818110610fcd57fe5b905060200201356001600160a01b0316336110417f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8a8a600081811061100f57fe5b905060200201356001600160a01b03168b8b600181811061102c57fe5b905060200201356001600160a01b03166134e6565b8560008151811061104e57fe5b60200260200101516135be565b61109a8287878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525030925061372f915050565b7f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6001600160a01b0316632e1a7d4d836001855103815181106110d957fe5b60200260200101516040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561111757600080fd5b505af115801561112b573d6000803e3d6000fd5b50505050611150848360018551038151811061114357fe5b60200260200101516131ca565b509695505050505050565b60606111887f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd848461396c565b90505b92915050565b60008060006111c17f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8f8f6134e6565b90506000876111d0578c6111d4565b6000195b6040805163d505accf60e01b815233600482015230602482015260448101839052606481018c905260ff8a16608482015260a4810189905260c4810188905290519192506001600160a01b0384169163d505accf9160e48082019260009290919082900301818387803b15801561124a57600080fd5b505af115801561125e573d6000803e3d6000fd5b505050506112718f8f8f8f8f8f8f612536565b809450819550505050509b509b9950505050505050505050565b606081428110156112d1576040805162461bcd60e51b81526020600482015260186024820152600080516020614697833981519152604482015290519081900360640190fd5b61132f7f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8988888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061339a92505050565b9150868260018451038151811061134257fe5b602002602001015110156113875760405162461bcd60e51b815260040180806020018281038252602b815260200180614614602b913960400191505060405180910390fd5b61139786866000818110610fcd57fe5b6111508287878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525089925061372f915050565b6060814281101561141c576040805162461bcd60e51b81526020600482015260186024820152600080516020614697833981519152604482015290519081900360640190fd5b6001600160a01b037f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd168686600019810181811061145657fe5b905060200201356001600160a01b03166001600160a01b0316146114c1576040805162461bcd60e51b815260206004820152601d60248201527f556e69737761705632526f757465723a20494e56414c49445f50415448000000604482015290519081900360640190fd5b61151f7f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8988888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061396c92505050565b9150868260008151811061152f57fe5b60200260200101511115610fbd5760405162461bcd60e51b81526004018080602001828103825260278152602001806145c76027913960400191505060405180910390fd5b6000806115c27f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8d7f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6134e6565b90506000866115d1578b6115d5565b6000195b6040805163d505accf60e01b815233600482015230602482015260448101839052606481018b905260ff8916608482015260a4810188905260c4810187905290519192506001600160a01b0384169163d505accf9160e48082019260009290919082900301818387803b15801561164b57600080fd5b505af115801561165f573d6000803e3d6000fd5b505050506116718d8d8d8d8d8d61202d565b9d9c50505050505050505050505050565b80428110156116c6576040805162461bcd60e51b81526020600482015260186024820152600080516020614697833981519152604482015290519081900360640190fd5b61173b858560008181106116d657fe5b905060200201356001600160a01b0316336117357f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8989600081811061171857fe5b905060200201356001600160a01b03168a8a600181811061102c57fe5b8a6135be565b60008585600019810181811061174d57fe5b905060200201356001600160a01b03166001600160a01b03166370a08231856040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156117a957600080fd5b505afa1580156117bd573d6000803e3d6000fd5b505050506040513d60208110156117d357600080fd5b50516040805160208881028281018201909352888252929350611815929091899189918291850190849080828437600092019190915250889250613aa4915050565b866118b8828888600019810181811061182a57fe5b905060200201356001600160a01b03166001600160a01b03166370a08231886040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561188657600080fd5b505afa15801561189a573d6000803e3d6000fd5b505050506040513d60208110156118b057600080fd5b505190613dc6565b10156118f55760405162461bcd60e51b815260040180806020018281038252602b815260200180614614602b913960400191505060405180910390fd5b5050505050505050565b8042811015611943576040805162461bcd60e51b81526020600482015260186024820152600080516020614697833981519152604482015290519081900360640190fd5b6001600160a01b037f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd168585600019810181811061197d57fe5b905060200201356001600160a01b03166001600160a01b0316146119e8576040805162461bcd60e51b815260206004820152601d60248201527f556e69737761705632526f757465723a20494e56414c49445f50415448000000604482015290519081900360640190fd5b6119f8858560008181106116d657fe5b611a36858580806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250309250613aa4915050565b60007f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611aa557600080fd5b505afa158015611ab9573d6000803e3d6000fd5b505050506040513d6020811015611acf57600080fd5b5051905086811015611b125760405162461bcd60e51b815260040180806020018281038252602b815260200180614614602b913960400191505060405180910390fd5b7f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6001600160a01b0316632e1a7d4d826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015611b7857600080fd5b505af1158015611b8c573d6000803e3d6000fd5b505050506118f584826131ca565b60608142811015611be0576040805162461bcd60e51b81526020600482015260186024820152600080516020614697833981519152604482015290519081900360640190fd5b7f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6001600160a01b031686866000818110611c1757fe5b905060200201356001600160a01b03166001600160a01b031614611c82576040805162461bcd60e51b815260206004820152601d60248201527f556e69737761705632526f757465723a20494e56414c49445f50415448000000604482015290519081900360640190fd5b611ce07f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd3488888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061339a92505050565b91508682600184510381518110611cf357fe5b60200260200101511015611d385760405162461bcd60e51b815260040180806020018281038252602b815260200180614614602b913960400191505060405180910390fd5b7f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6001600160a01b031663d0e30db083600081518110611d7457fe5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b158015611da757600080fd5b505af1158015611dbb573d6000803e3d6000fd5b50505050507f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6001600160a01b031663a9059cbb611e207f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8989600081811061171857fe5b84600081518110611e2d57fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015611e7b57600080fd5b505af1158015611e8f573d6000803e3d6000fd5b505050506040513d6020811015611ea557600080fd5b5051611ead57fe5b611eec8287878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525089925061372f915050565b5095945050505050565b6000610e14848484613e1e565b60608142811015611f49576040805162461bcd60e51b81526020600482015260186024820152600080516020614697833981519152604482015290519081900360640190fd5b611fa77f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8988888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061396c92505050565b91508682600081518110611fb757fe5b602002602001015111156113875760405162461bcd60e51b81526004018080602001828103825260278152602001806145c76027913960400191505060405180910390fd5b7f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd81565b6000610e14848484613ef6565b60008142811015612073576040805162461bcd60e51b81526020600482015260186024820152600080516020614697833981519152604482015290519081900360640190fd5b6120a2887f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd8989893089612536565b90508092505061212b88858a6001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156120fa57600080fd5b505afa15801561210e573d6000803e3d6000fd5b505050506040513d602081101561212457600080fd5b5051613049565b7f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6001600160a01b0316632e1a7d4d836040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561219157600080fd5b505af11580156121a5573d6000803e3d6000fd5b5050505061115084836131ca565b80428110156121f7576040805162461bcd60e51b81526020600482015260186024820152600080516020614697833981519152604482015290519081900360640190fd5b7f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6001600160a01b03168585600081811061222e57fe5b905060200201356001600160a01b03166001600160a01b031614612299576040805162461bcd60e51b815260206004820152601d60248201527f556e69737761705632526f757465723a20494e56414c49445f50415448000000604482015290519081900360640190fd5b60003490507f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156122f957600080fd5b505af115801561230d573d6000803e3d6000fd5b50505050507f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6001600160a01b031663a9059cbb6123727f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8989600081811061171857fe5b836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b1580156123b957600080fd5b505af11580156123cd573d6000803e3d6000fd5b505050506040513d60208110156123e357600080fd5b50516123eb57fe5b6000868660001981018181106123fd57fe5b905060200201356001600160a01b03166001600160a01b03166370a08231866040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561245957600080fd5b505afa15801561246d573d6000803e3d6000fd5b505050506040513d602081101561248357600080fd5b505160408051602089810282810182019093528982529293506124c59290918a918a918291850190849080828437600092019190915250899250613aa4915050565b876118b882898960001981018181106124da57fe5b905060200201356001600160a01b03166001600160a01b03166370a08231896040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561188657600080fd5b600080824281101561257d576040805162461bcd60e51b81526020600482015260186024820152600080516020614697833981519152604482015290519081900360640190fd5b60006125aa7f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8c8c6134e6565b604080516323b872dd60e01b81523360048201526001600160a01b03831660248201819052604482018d9052915192935090916323b872dd916064808201926020929091908290030181600087803b15801561260557600080fd5b505af1158015612619573d6000803e3d6000fd5b505050506040513d602081101561262f57600080fd5b5050604080517f89afcb440000000000000000000000000000000000000000000000000000000081526001600160a01b03888116600483015282516000938493928616926389afcb44926024808301939282900301818787803b15801561269557600080fd5b505af11580156126a9573d6000803e3d6000fd5b505050506040513d60408110156126bf57600080fd5b508051602090910151909250905060006126d98e8e613f9c565b509050806001600160a01b03168e6001600160a01b0316146126fc5781836126ff565b82825b90975095508a8710156127435760405162461bcd60e51b81526004018080602001828103825260268152602001806145ee6026913960400191505060405180910390fd5b898610156127825760405162461bcd60e51b81526004018080602001828103825260268152602001806145546026913960400191505060405180910390fd5b505050505097509795505050505050565b7f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd81565b60606111887f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd848461339a565b60008060006128347f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8e7f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6134e6565b9050600087612843578c612847565b6000195b6040805163d505accf60e01b815233600482015230602482015260448101839052606481018c905260ff8a16608482015260a4810189905260c4810188905290519192506001600160a01b0384169163d505accf9160e48082019260009290919082900301818387803b1580156128bd57600080fd5b505af11580156128d1573d6000803e3d6000fd5b505050506128e38e8e8e8e8e8e610ced565b909f909e509c50505050505050505050505050565b60008060008342811015612941576040805162461bcd60e51b81526020600482015260186024820152600080516020614697833981519152604482015290519081900360640190fd5b61294f8c8c8c8c8c8c61407a565b909450925060006129817f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8e8e6134e6565b905061298f8d3383886135be565b61299b8c3383876135be565b806001600160a01b0316636a627842886040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050602060405180830381600087803b1580156129ea57600080fd5b505af11580156129fe573d6000803e3d6000fd5b505050506040513d6020811015612a1457600080fd5b5051949d939c50939a509198505050505050505050565b60008060008342811015612a74576040805162461bcd60e51b81526020600482015260186024820152600080516020614697833981519152604482015290519081900360640190fd5b612aa28a7f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd8b348c8c61407a565b90945092506000612af47f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8c7f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6134e6565b9050612b028b3383886135be565b7f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b158015612b5d57600080fd5b505af1158015612b71573d6000803e3d6000fd5b50505050507f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6001600160a01b031663a9059cbb82866040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015612bed57600080fd5b505af1158015612c01573d6000803e3d6000fd5b505050506040513d6020811015612c1757600080fd5b5051612c1f57fe5b806001600160a01b0316636a627842886040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050602060405180830381600087803b158015612c6e57600080fd5b505af1158015612c82573d6000803e3d6000fd5b505050506040513d6020811015612c9857600080fd5b5051925034841015612cb057612cb0338534036131ca565b505096509650969350505050565b60608142811015612d04576040805162461bcd60e51b81526020600482015260186024820152600080516020614697833981519152604482015290519081900360640190fd5b7f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6001600160a01b031686866000818110612d3b57fe5b905060200201356001600160a01b03166001600160a01b031614612da6576040805162461bcd60e51b815260206004820152601d60248201527f556e69737761705632526f757465723a20494e56414c49445f50415448000000604482015290519081900360640190fd5b612e047f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8888888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061396c92505050565b91503482600081518110612e1457fe5b60200260200101511115612e595760405162461bcd60e51b81526004018080602001828103825260278152602001806145c76027913960400191505060405180910390fd5b7f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6001600160a01b031663d0e30db083600081518110612e9557fe5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b158015612ec857600080fd5b505af1158015612edc573d6000803e3d6000fd5b50505050507f0000000000000000000000006b3bd0478df0ec4984b168db0e12a539cc0c83cd6001600160a01b031663a9059cbb612f417f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8989600081811061171857fe5b84600081518110612f4e57fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015612f9c57600080fd5b505af1158015612fb0573d6000803e3d6000fd5b505050506040513d6020811015612fc657600080fd5b5051612fce57fe5b61300d8287878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525089925061372f915050565b8160008151811061301a57fe5b6020026020010151341115611eec57611eec338360008151811061303a57fe5b602002602001015134036131ca565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001781529251825160009485949389169392918291908083835b602083106130f35780518252601f1990920191602091820191016130d4565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613155576040519150601f19603f3d011682016040523d82523d6000602084013e61315a565b606091505b5091509150818015613188575080511580613188575080806020019051602081101561318557600080fd5b50515b6131c35760405162461bcd60e51b815260040180806020018281038252602d81526020018061463f602d913960400191505060405180910390fd5b5050505050565b604080516000808252602082019092526001600160a01b0384169083906040518082805190602001908083835b602083106132165780518252601f1990920191602091820191016131f7565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114613278576040519150601f19603f3d011682016040523d82523d6000602084013e61327d565b606091505b50509050806132bd5760405162461bcd60e51b81526004018080602001828103825260348152602001806144fb6034913960400191505060405180910390fd5b505050565b60008084116133025760405162461bcd60e51b815260040180806020018281038252602b81526020018061466c602b913960400191505060405180910390fd5b6000831180156133125750600082115b61334d5760405162461bcd60e51b815260040180806020018281038252602881526020018061457a6028913960400191505060405180910390fd5b600061335b856103e561430b565b90506000613369828561430b565b905060006133838361337d886103e861430b565b90614377565b905080828161338e57fe5b04979650505050505050565b60606002825110156133f3576040805162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a20494e56414c49445f504154480000604482015290519081900360640190fd5b815167ffffffffffffffff8111801561340b57600080fd5b50604051908082528060200260200182016040528015613435578160200160208202803683370190505b509050828160008151811061344657fe5b60200260200101818152505060005b60018351038110156134de576000806134988786858151811061347457fe5b602002602001015187866001018151811061348b57fe5b60200260200101516143cf565b915091506134ba8484815181106134ab57fe5b602002602001015183836132c2565b8484600101815181106134c957fe5b60209081029190910101525050600101613455565b509392505050565b60008060006134f58585613f9c565b604080516bffffffffffffffffffffffff19606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501207fff0000000000000000000000000000000000000000000000000000000000000060688401529a90941b9093166069840152607d8301989098527f930f72af52e9f7553fd568961dff48b9d73ba993c0ddc3f637f6af07a34e97bd609d808401919091528851808403909101815260bd909201909752805196019590952095945050505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166323b872dd60e01b178152925182516000948594938a169392918291908083835b602083106136575780518252601f199092019160209182019101613638565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146136b9576040519150601f19603f3d011682016040523d82523d6000602084013e6136be565b606091505b50915091508180156136ec5750805115806136ec57508080602001905160208110156136e957600080fd5b50515b6137275760405162461bcd60e51b81526004018080602001828103825260318152602001806144ca6031913960400191505060405180910390fd5b505050505050565b60005b60018351038110156139665760008084838151811061374d57fe5b602002602001015185846001018151811061376457fe5b602002602001015191509150600061377c8383613f9c565b509050600087856001018151811061379057fe5b60200260200101519050600080836001600160a01b0316866001600160a01b0316146137be578260006137c2565b6000835b91509150600060028a510388106137d9578861381a565b61381a7f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd878c8b6002018151811061380d57fe5b60200260200101516134e6565b90506138477f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd88886134e6565b6001600160a01b031663022c0d9f84848460006040519080825280601f01601f191660200182016040528015613884576020820181803683370190505b506040518563ffffffff1660e01b815260040180858152602001848152602001836001600160a01b0316815260200180602001828103825283818151815260200191508051906020019080838360005b838110156138ec5781810151838201526020016138d4565b50505050905090810190601f1680156139195780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b15801561393b57600080fd5b505af115801561394f573d6000803e3d6000fd5b505060019099019850613732975050505050505050565b50505050565b60606002825110156139c5576040805162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a20494e56414c49445f504154480000604482015290519081900360640190fd5b815167ffffffffffffffff811180156139dd57600080fd5b50604051908082528060200260200182016040528015613a07578160200160208202803683370190505b5090508281600183510381518110613a1b57fe5b60209081029190910101528151600019015b80156134de57600080613a5d87866001860381518110613a4957fe5b602002602001015187868151811061348b57fe5b91509150613a7f848481518110613a7057fe5b60200260200101518383613e1e565b846001850381518110613a8e57fe5b6020908102919091010152505060001901613a2d565b60005b60018351038110156132bd57600080848381518110613ac257fe5b6020026020010151858460010181518110613ad957fe5b6020026020010151915091506000613af18383613f9c565b5090506000613b217f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd85856134e6565b9050600080600080846001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b158015613b6257600080fd5b505afa158015613b76573d6000803e3d6000fd5b505050506040513d6060811015613b8c57600080fd5b5080516020909101516dffffffffffffffffffffffffffff91821693501690506000806001600160a01b038a811690891614613bc9578284613bcc565b83835b91509150613c21828b6001600160a01b03166370a082318a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561188657600080fd5b9550613c2e8683836132c2565b945050505050600080856001600160a01b0316886001600160a01b031614613c5857826000613c5c565b6000835b91509150600060028c51038a10613c73578a613ca7565b613ca77f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd898e8d6002018151811061380d57fe5b60408051600080825260208201928390527f022c0d9f00000000000000000000000000000000000000000000000000000000835260248201878152604483018790526001600160a01b038086166064850152608060848501908152845160a48601819052969750908c169563022c0d9f958a958a958a9591949193919260c486019290918190849084905b83811015613d4a578181015183820152602001613d32565b50505050905090810190601f168015613d775780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b158015613d9957600080fd5b505af1158015613dad573d6000803e3d6000fd5b50506001909b019a50613aa79950505050505050505050565b8082038281111561118b576040805162461bcd60e51b815260206004820152601560248201527f64732d6d6174682d7375622d756e646572666c6f770000000000000000000000604482015290519081900360640190fd5b6000808411613e5e5760405162461bcd60e51b815260040180806020018281038252602c81526020018061449e602c913960400191505060405180910390fd5b600083118015613e6e5750600082115b613ea95760405162461bcd60e51b815260040180806020018281038252602881526020018061457a6028913960400191505060405180910390fd5b6000613ec16103e8613ebb868861430b565b9061430b565b90506000613ed56103e5613ebb8689613dc6565b9050613eec6001828481613ee557fe5b0490614377565b9695505050505050565b6000808411613f365760405162461bcd60e51b81526004018080602001828103825260258152602001806145a26025913960400191505060405180910390fd5b600083118015613f465750600082115b613f815760405162461bcd60e51b815260040180806020018281038252602881526020018061457a6028913960400191505060405180910390fd5b82613f8c858461430b565b81613f9357fe5b04949350505050565b600080826001600160a01b0316846001600160a01b03161415613ff05760405162461bcd60e51b815260040180806020018281038252602581526020018061452f6025913960400191505060405180910390fd5b826001600160a01b0316846001600160a01b031610614010578284614013565b83835b90925090506001600160a01b038216614073576040805162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a205a45524f5f414444524553530000604482015290519081900360640190fd5b9250929050565b60008060006001600160a01b03167f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd6001600160a01b031663e6a439058a8a6040518363ffffffff1660e01b815260040180836001600160a01b03168152602001826001600160a01b031681526020019250505060206040518083038186803b15801561410657600080fd5b505afa15801561411a573d6000803e3d6000fd5b505050506040513d602081101561413057600080fd5b50516001600160a01b031614156141ee577f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd6001600160a01b031663c9c6539689896040518363ffffffff1660e01b815260040180836001600160a01b03168152602001826001600160a01b0316815260200192505050602060405180830381600087803b1580156141c157600080fd5b505af11580156141d5573d6000803e3d6000fd5b505050506040513d60208110156141eb57600080fd5b50505b60008061421c7f00000000000000000000000017854c8d5a41d5a89b275386e24b2f38fd0afbdd8b8b6143cf565b9150915081600014801561422e575080155b1561423e578793508692506142fe565b600061424b898484613ef6565b905087811161429e57858110156142935760405162461bcd60e51b81526004018080602001828103825260268152602001806145546026913960400191505060405180910390fd5b8894509250826142fc565b60006142ab898486613ef6565b9050898111156142b757fe5b878110156142f65760405162461bcd60e51b81526004018080602001828103825260268152602001806145ee6026913960400191505060405180910390fd5b94508793505b505b5050965096945050505050565b60008115806143265750508082028282828161432357fe5b04145b61118b576040805162461bcd60e51b815260206004820152601460248201527f64732d6d6174682d6d756c2d6f766572666c6f77000000000000000000000000604482015290519081900360640190fd5b8082018281101561118b576040805162461bcd60e51b815260206004820152601460248201527f64732d6d6174682d6164642d6f766572666c6f77000000000000000000000000604482015290519081900360640190fd5b60008060006143de8585613f9c565b5090506000806143ef8888886134e6565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561442757600080fd5b505afa15801561443b573d6000803e3d6000fd5b505050506040513d606081101561445157600080fd5b5080516020909101516dffffffffffffffffffffffffffff91821693501690506001600160a01b038781169084161461448b57808261448e565b81815b9099909850965050505050505056fe556e697377617056324c6962726172793a20494e53554646494349454e545f4f55545055545f414d4f554e545472616e7366657248656c7065723a3a7472616e7366657246726f6d3a207472616e7366657246726f6d206661696c65645472616e7366657248656c7065723a3a736166655472616e736665724554483a20455448207472616e73666572206661696c6564556e697377617056324c6962726172793a204944454e544943414c5f414444524553534553556e69737761705632526f757465723a20494e53554646494349454e545f425f414d4f554e54556e697377617056324c6962726172793a20494e53554646494349454e545f4c4951554944495459556e697377617056324c6962726172793a20494e53554646494349454e545f414d4f554e54556e69737761705632526f757465723a204558434553534956455f494e5055545f414d4f554e54556e69737761705632526f757465723a20494e53554646494349454e545f415f414d4f554e54556e69737761705632526f757465723a20494e53554646494349454e545f4f55545055545f414d4f554e545472616e7366657248656c7065723a3a736166655472616e736665723a207472616e73666572206661696c6564556e697377617056324c6962726172793a20494e53554646494349454e545f494e5055545f414d4f554e54556e69737761705632526f757465723a20455850495245440000000000000000a26469706673582212205eea4cac156fddbe64c63fb32562fca1663c9f1a2863e7bb53d2820aec35c7c264736f6c63430007060033