Transactions
Token Transfers
Tokens
Internal Transactions
Coin Balance History
Code
Read Contract
Write Contract
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- PulseXSwapRouter
- Optimization enabled
- true
- Compiler version
- v0.7.6+commit.7338295f
- Optimization runs
- 1000000
- EVM Version
- istanbul
- Verified at
- 2024-09-20T16:27:51.147511Z
Constructor Arguments
0x0000000000000000000000001715a3e4a142d8b698131108995174f37aeba10d00000000000000000000000029ea7545def87022badc76323f373ea1e707c5230000000000000000000000001973e61a0518a3cfcbfb7a840ee9ac7ba35562a0000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a27
Arg [0] (address) : 0x1715a3e4a142d8b698131108995174f37aeba10d
Arg [1] (address) : 0x29ea7545def87022badc76323f373ea1e707c523
Arg [2] (address) : 0x1973e61a0518a3cfcbfb7a840ee9ac7ba35562a0
Arg [3] (address) : 0xa1077a294dde1b09bb078844df40758a5d0f9a27
contracts/PulseXSwapRouter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import './interfaces/IPulseXSwapRouter.sol';
import './V1SwapRouter.sol';
import './V2SwapRouter.sol';
import './StableSwapRouter.sol';
import './base/MulticallExtended.sol';
import './base/SelfPermit.sol';
/// @title PulseX V1, V2 and Stable Swap Router
contract PulseXSwapRouter is IPulseXSwapRouter, V1SwapRouter, V2SwapRouter, StableSwapRouter, MulticallExtended, SelfPermit {
constructor(
address _factoryV1,
address factoryV2,
address _stableInfo,
address _WETH9
) ImmutableState(_factoryV1, factoryV2, _WETH9) StableSwapRouter(_stableInfo) {}
}
contracts/base/SelfPermit.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/drafts/IERC20Permit.sol';
import '../interfaces/ISelfPermit.sol';
import '../interfaces/IERC20PermitAllowed.sol';
/// @title Self Permit
/// @notice Functionality to call permit on any EIP-2612-compliant token for use in the route
/// @dev These functions are expected to be embedded in multicalls to allow EOAs to approve a contract and call a function
/// that requires an approval in a single transaction.
abstract contract SelfPermit is ISelfPermit {
/// @inheritdoc ISelfPermit
function selfPermit(
address token,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public payable override {
IERC20Permit(token).permit(msg.sender, address(this), value, deadline, v, r, s);
}
/// @inheritdoc ISelfPermit
function selfPermitIfNecessary(
address token,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable override {
if (IERC20(token).allowance(msg.sender, address(this)) < value) selfPermit(token, value, deadline, v, r, s);
}
/// @inheritdoc ISelfPermit
function selfPermitAllowed(
address token,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) public payable override {
IERC20PermitAllowed(token).permit(msg.sender, address(this), nonce, expiry, true, v, r, s);
}
/// @inheritdoc ISelfPermit
function selfPermitAllowedIfNecessary(
address token,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) external payable override {
if (IERC20(token).allowance(msg.sender, address(this)) < type(uint256).max)
selfPermitAllowed(token, nonce, expiry, v, r, s);
}
}
contracts/libraries/PulseXLibraryV2.sol
pragma solidity >=0.5.0;
import '../interfaces/IPulseXPair.sol';
import './LowGasSafeMath.sol';
library PulseXLibraryV2 {
using LowGasSafeMath for uint;
// 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);
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0));
}
// 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(uint(keccak256(abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encodePacked(token0, token1)),
hex'5dff1ac2d132f5ac2841294c6e9fc0ebafae8d447fac7996ef21c21112f411f1' // 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, ) = IPulseXPair(pairFor(factory, tokenA, tokenB)).getReserves();
(reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
// 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, 'INSUFFICIENT_INPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0);
uint256 amountInWithFee = amountIn.mul(9971);
uint256 numerator = amountInWithFee.mul(reserveOut);
uint256 denominator = reserveIn.mul(10000).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, 'INSUFFICIENT_OUTPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0);
uint256 numerator = reserveIn.mul(amountOut).mul(10000);
uint256 denominator = reserveOut.sub(amountOut).mul(9971);
amountIn = (numerator / denominator).add(1);
}
// 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);
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);
}
}
}
contracts/base/ImmutableState.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
import '../interfaces/IImmutableState.sol';
/// @title Immutable state
/// @notice Immutable state used by the swap router
abstract contract ImmutableState is IImmutableState {
/// @inheritdoc IImmutableState
address public immutable override factoryV1;
/// @inheritdoc IImmutableState
address public immutable override factoryV2;
/// @inheritdoc IImmutableState
address public immutable override WETH9;
constructor(address _factoryV1, address _factoryV2, address _WETH9) {
factoryV1 = _factoryV1;
factoryV2 = _factoryV2;
WETH9 = _WETH9;
}
}
contracts/interfaces/IPeripheryPaymentsExtended.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
import './IPeripheryPayments.sol';
/// @title Periphery Payments Extended
/// @notice Functions to ease deposits and withdrawals of ETH and tokens
interface IPeripheryPaymentsExtended is IPeripheryPayments {
/// @notice Unwraps the contract's WETH9 balance and sends it to msg.sender as ETH.
/// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users.
/// @param amountMinimum The minimum amount of WETH9 to unwrap
function unwrapWETH9(uint256 amountMinimum) external payable;
/// @notice Wraps the contract's ETH balance into WETH9
/// @dev The resulting WETH9 is custodied by the router, thus will require further distribution
/// @param value The amount of ETH to wrap
function wrapETH(uint256 value) external payable;
/// @notice Transfers the full amount of a token held by this contract to msg.sender
/// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users
/// @param token The contract address of the token which will be transferred to msg.sender
/// @param amountMinimum The minimum amount of token required for a transfer
function sweepToken(address token, uint256 amountMinimum) external payable;
/// @notice Transfers the specified amount of a token from the msg.sender to address(this)
/// @param token The token to pull
/// @param value The amount to pay
function pull(address token, uint256 value) external payable;
}
contracts/interfaces/IV2SwapRouter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via PulseX V2
interface IV2SwapRouter {
/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance,
/// and swap the entire amount, enabling contracts to send tokens before calling this function.
/// @param amountIn The amount of token to swap
/// @param amountOutMin The minimum amount of output that must be received
/// @param path The ordered list of tokens to swap through
/// @param to The recipient address
/// @return amountOut The amount of the received token
function swapExactTokensForTokensV2(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to
) external payable returns (uint256 amountOut);
/// @notice Swaps as little as possible of one token for an exact amount of another token
/// @param amountOut The amount of token to swap for
/// @param amountInMax The maximum amount of input that the caller will pay
/// @param path The ordered list of tokens to swap through
/// @param to The recipient address
/// @return amountIn The amount of token to pay
function swapTokensForExactTokensV2(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to
) external payable returns (uint256 amountIn);
}
contracts/interfaces/IPulseXPair.sol
pragma solidity >=0.5.0;
interface IPulseXPair {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint 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 (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint 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 (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, uint amount0, uint amount1, address indexed senderOrigin);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to, address indexed senderOrigin);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint);
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 (uint);
function price1CumulativeLast() external view returns (uint);
function kLast() external view returns (uint);
function mint(address to, address senderOrigin) external returns (uint liquidity);
function burn(address to, address senderOrigin) external returns (uint amount0, uint amount1);
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
function skim(address to) external;
function sync() external;
function initialize(address, address) external;
}
contracts/StableSwapRouter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
import './interfaces/IStableSwapRouter.sol';
import './interfaces/IStableSwap.sol';
import './libraries/StableSwapLibrary.sol';
import './libraries/Constants.sol';
import './base/PeripheryPaymentsWithFeeExtended.sol';
/// @title PulseX Stable Swap Router
abstract contract StableSwapRouter is IStableSwapRouter, PeripheryPaymentsWithFeeExtended, ReentrancyGuard {
address public stableSwapInfo;
event SetStableSwap(address indexed factory, address indexed info);
constructor( address _stableSwapInfo) {
stableSwapInfo = _stableSwapInfo;
}
/// `refundETH` should be called at very end of all swaps
function _swap(
address[] memory path,
address[] memory swapContracts
) private {
for (uint256 i; i < path.length - 1; i++) {
(address input, address output) = (path[i], path[i + 1]);
(uint256 k, uint256 j) = StableSwapLibrary.getStableInfo(swapContracts[i], input, output);
uint256 amountIn_ = IERC20(input).balanceOf(address(this));
TransferHelper.safeApprove(input, swapContracts[i], amountIn_);
IStableSwap(swapContracts[i]).exchange(k, j, amountIn_, 0);
}
}
function exactInputStableSwap(
address[] calldata path,
address[] calldata swapContracts,
uint256 amountIn,
uint256 amountOutMin,
address to
) external payable override nonReentrant returns (uint256 amountOut) {
IERC20 srcToken = IERC20(path[0]);
IERC20 dstToken = IERC20(path[path.length - 1]);
// use amountIn == Constants.CONTRACT_BALANCE as a flag to swap the entire balance of the contract
bool hasAlreadyPaid;
if (amountIn == Constants.CONTRACT_BALANCE) {
hasAlreadyPaid = true;
amountIn = srcToken.balanceOf(address(this));
}
if (!hasAlreadyPaid) {
pay(address(srcToken), msg.sender, address(this), amountIn);
}
_swap(path, swapContracts);
amountOut = dstToken.balanceOf(address(this));
require(amountOut >= amountOutMin);
// find and replace to addresses
if (to == Constants.MSG_SENDER) to = msg.sender;
else if (to == Constants.ADDRESS_THIS) to = address(this);
if (to != address(this)) pay(address(dstToken), address(this), to, amountOut);
}
function exactOutputStableSwap(
address[] calldata path,
address[] calldata swapContracts,
uint256 amountOut,
uint256 amountInMax,
address to
) external payable override nonReentrant returns (uint256 amountIn) {
amountIn = StableSwapLibrary.getStableAmountsIn(swapContracts[0], stableSwapInfo, path, amountOut)[0];
require(amountIn <= amountInMax);
pay(path[0], msg.sender, address(this), amountIn);
_swap(path, swapContracts);
// find and replace to addresses
if (to == Constants.MSG_SENDER) to = msg.sender;
else if (to == Constants.ADDRESS_THIS) to = address(this);
if (to != address(this)) pay(path[path.length - 1], address(this), to, amountOut);
}
}
contracts/base/PeripheryPaymentsExtended.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
import './PeripheryPayments.sol';
import '../libraries/TransferHelper.sol';
import '../interfaces/IPeripheryPaymentsExtended.sol';
abstract contract PeripheryPaymentsExtended is IPeripheryPaymentsExtended, PeripheryPayments {
/// @inheritdoc IPeripheryPaymentsExtended
function unwrapWETH9(uint256 amountMinimum) external payable override {
unwrapWETH9(amountMinimum, msg.sender);
}
/// @inheritdoc IPeripheryPaymentsExtended
function wrapETH(uint256 value) external payable override {
IWETH9(WETH9).deposit{value: value}();
}
/// @inheritdoc IPeripheryPaymentsExtended
function sweepToken(address token, uint256 amountMinimum) external payable override {
sweepToken(token, amountMinimum, msg.sender);
}
/// @inheritdoc IPeripheryPaymentsExtended
function pull(address token, uint256 value) external payable override {
TransferHelper.safeTransferFrom(token, msg.sender, address(this), value);
}
}
contracts/base/BlockTimestamp.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
/// @title Function for getting block timestamp
/// @dev Base contract that is overridden for tests
abstract contract BlockTimestamp {
/// @dev Method that exists purely to be overridden for tests
/// @return The current block timestamp
function _blockTimestamp() internal view virtual returns (uint256) {
return block.timestamp;
}
}
contracts/interfaces/IStableSwapInfo.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.7.6;
pragma abicoder v2;
interface IStableSwapInfo {
function get_dx(
address _swap,
uint256 i,
uint256 j,
uint256 dy,
uint256 max_dx
) external view returns (uint256);
}
contracts/interfaces/IMulticallExtended.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
import './IMulticall.sol';
/// @title MulticallExtended interface
/// @notice Enables calling multiple methods in a single call to the contract with optional validation
interface IMulticallExtended is IMulticall {
/// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
/// @dev The `msg.value` should not be trusted for any method callable from multicall.
/// @param deadline The time by which this function must be called before failing
/// @param data The encoded function data for each of the calls to make to this contract
/// @return results The results from each of the calls passed in via data
function multicall(uint256 deadline, bytes[] calldata data) external payable returns (bytes[] memory results);
/// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
/// @dev The `msg.value` should not be trusted for any method callable from multicall.
/// @param previousBlockhash The expected parent blockHash
/// @param data The encoded function data for each of the calls to make to this contract
/// @return results The results from each of the calls passed in via data
function multicall(bytes32 previousBlockhash, bytes[] calldata data)
external
payable
returns (bytes[] memory results);
}
contracts/libraries/TransferHelper.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.0;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
library TransferHelper {
/// @notice Transfers tokens from the targeted address to the given destination
/// @notice Errors with 'STF' if transfer fails
/// @param token The contract address of the token to be transferred
/// @param from The originating address from which the tokens will be transferred
/// @param to The destination address of the transfer
/// @param value The amount to be transferred
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
(bool success, bytes memory data) =
token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF');
}
/// @notice Transfers tokens from msg.sender to a recipient
/// @dev Errors with ST if transfer fails
/// @param token The contract address of the token which will be transferred
/// @param to The recipient of the transfer
/// @param value The value of the transfer
function safeTransfer(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'ST');
}
/// @notice Approves the stipulated contract to spend the given allowance in the given token
/// @dev Errors with 'SA' if transfer fails
/// @param token The contract address of the token to be approved
/// @param to The target of the approval
/// @param value The amount of the given token the target will be allowed to spend
function safeApprove(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'SA');
}
/// @notice Transfers ETH to the recipient address
/// @dev Fails with `STE`
/// @param to The destination of the transfer
/// @param value The value to be transferred
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, 'STE');
}
}
contracts/base/Multicall.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import '../interfaces/IMulticall.sol';
/// @title Multicall
/// @notice Enables calling multiple methods in a single call to the contract
abstract contract Multicall is IMulticall {
/// @inheritdoc IMulticall
function multicall(bytes[] calldata data) public payable override returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
(bool success, bytes memory result) = address(this).delegatecall(data[i]);
if (!success) {
// Next 5 lines from https://ethereum.stackexchange.com/a/83577
if (result.length < 68) revert();
assembly {
result := add(result, 0x04)
}
revert(abi.decode(result, (string)));
}
results[i] = result;
}
}
}
contracts/interfaces/ISelfPermit.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
/// @title Self Permit
/// @notice Functionality to call permit on any EIP-2612-compliant token for use in the route
interface ISelfPermit {
/// @notice Permits this contract to spend a given token from `msg.sender`
/// @dev The `owner` is always msg.sender and the `spender` is always address(this).
/// @param token The address of the token spent
/// @param value The amount that can be spent of token
/// @param deadline A timestamp, the current blocktime must be less than or equal to this timestamp
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function selfPermit(
address token,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
/// @notice Permits this contract to spend a given token from `msg.sender`
/// @dev The `owner` is always msg.sender and the `spender` is always address(this).
/// Can be used instead of #selfPermit to prevent calls from failing due to a frontrun of a call to #selfPermit
/// @param token The address of the token spent
/// @param value The amount that can be spent of token
/// @param deadline A timestamp, the current blocktime must be less than or equal to this timestamp
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function selfPermitIfNecessary(
address token,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
/// @notice Permits this contract to spend the sender's tokens for permit signatures that have the `allowed` parameter
/// @dev The `owner` is always msg.sender and the `spender` is always address(this)
/// @param token The address of the token spent
/// @param nonce The current nonce of the owner
/// @param expiry The timestamp at which the permit is no longer valid
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function selfPermitAllowed(
address token,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
/// @notice Permits this contract to spend the sender's tokens for permit signatures that have the `allowed` parameter
/// @dev The `owner` is always msg.sender and the `spender` is always address(this)
/// Can be used instead of #selfPermitAllowed to prevent calls from failing due to a frontrun of a call to #selfPermitAllowed.
/// @param token The address of the token spent
/// @param nonce The current nonce of the owner
/// @param expiry The timestamp at which the permit is no longer valid
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function selfPermitAllowedIfNecessary(
address token,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
}
contracts/interfaces/IPeripheryPaymentsWithFeeExtended.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
import './IPeripheryPaymentsWithFee.sol';
import './IPeripheryPaymentsExtended.sol';
/// @title Periphery Payments With Fee Extended
/// @notice Functions to ease deposits and withdrawals of ETH
interface IPeripheryPaymentsWithFeeExtended is IPeripheryPaymentsExtended, IPeripheryPaymentsWithFee {
/// @notice Unwraps the contract's WETH9 balance and sends it to msg.sender as ETH, with a percentage between
/// 0 (exclusive), and 1 (inclusive) going to feeRecipient
/// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users.
function unwrapWETH9WithFee(
uint256 amountMinimum,
uint256 feeBips,
address feeRecipient
) external payable;
/// @notice Transfers the full amount of a token held by this contract to msg.sender, with a percentage between
/// 0 (exclusive) and 1 (inclusive) going to feeRecipient
/// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users
function sweepTokenWithFee(
address token,
uint256 amountMinimum,
uint256 feeBips,
address feeRecipient
) external payable;
}
contracts/interfaces/IPeripheryPaymentsWithFee.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
import './IPeripheryPayments.sol';
/// @title Periphery Payments
/// @notice Functions to ease deposits and withdrawals of ETH
interface IPeripheryPaymentsWithFee is IPeripheryPayments {
/// @notice Unwraps the contract's WETH9 balance and sends it to recipient as ETH, with a percentage between
/// 0 (exclusive), and 1 (inclusive) going to feeRecipient
/// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users.
function unwrapWETH9WithFee(
uint256 amountMinimum,
address recipient,
uint256 feeBips,
address feeRecipient
) external payable;
/// @notice Transfers the full amount of a token held by this contract to recipient, with a percentage between
/// 0 (exclusive) and 1 (inclusive) going to feeRecipient
/// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users
function sweepTokenWithFee(
address token,
uint256 amountMinimum,
address recipient,
uint256 feeBips,
address feeRecipient
) external payable;
}
contracts/libraries/PulseXLibraryV1.sol
pragma solidity >=0.5.0;
import '../interfaces/IPulseXPair.sol';
import './LowGasSafeMath.sol';
library PulseXLibraryV1 {
using LowGasSafeMath for uint;
// 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);
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0));
}
// 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(uint(keccak256(abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encodePacked(token0, token1)),
hex'59fffffddd756cba9095128e53f3291a6ba38b21e3df744936e7289326555d62' // 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, ) = IPulseXPair(pairFor(factory, tokenA, tokenB)).getReserves();
(reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
// 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, 'INSUFFICIENT_INPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0);
uint256 amountInWithFee = amountIn.mul(9971);
uint256 numerator = amountInWithFee.mul(reserveOut);
uint256 denominator = reserveIn.mul(10000).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, 'INSUFFICIENT_OUTPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0);
uint256 numerator = reserveIn.mul(amountOut).mul(10000);
uint256 denominator = reserveOut.sub(amountOut).mul(9971);
amountIn = (numerator / denominator).add(1);
}
// 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);
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);
}
}
}
contracts/V2SwapRouter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import './libraries/LowGasSafeMath.sol';
import './interfaces/IV2SwapRouter.sol';
import './base/ImmutableState.sol';
import './base/PeripheryPaymentsWithFeeExtended.sol';
import './libraries/Constants.sol';
import './libraries/PulseXLibraryV2.sol';
/// @title PulseX V2 Swap Router
/// @notice Router for stateless execution of swaps against PulseX V2
abstract contract V2SwapRouter is IV2SwapRouter, ImmutableState, PeripheryPaymentsWithFeeExtended {
using LowGasSafeMath for uint256;
// supports fee-on-transfer tokens
// requires the initial amount to have already been sent to the first pair
function _swapV2(address[] memory path, address _to) private {
for (uint256 i; i < path.length - 1; i++) {
(address input, address output) = (path[i], path[i + 1]);
(address token0, ) = PulseXLibraryV2.sortTokens(input, output);
IPulseXPair pair = IPulseXPair(PulseXLibraryV2.pairFor(factoryV2, 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 = PulseXLibraryV2.getAmountOut(amountInput, reserveInput, reserveOutput);
}
(uint256 amount0Out, uint256 amount1Out) =
input == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0));
address to = i < path.length - 2 ? PulseXLibraryV2.pairFor(factoryV2, output, path[i + 2]) : _to;
pair.swap(amount0Out, amount1Out, to, new bytes(0));
}
}
/// @inheritdoc IV2SwapRouter
function swapExactTokensForTokensV2(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to
) external payable override returns (uint256 amountOut) {
// use amountIn == Constants.CONTRACT_BALANCE as a flag to swap the entire balance of the contract
bool hasAlreadyPaid;
if (amountIn == Constants.CONTRACT_BALANCE) {
hasAlreadyPaid = true;
amountIn = IERC20(path[0]).balanceOf(address(this));
}
pay(
path[0],
hasAlreadyPaid ? address(this) : msg.sender,
PulseXLibraryV2.pairFor(factoryV2, path[0], path[1]),
amountIn
);
// find and replace to addresses
if (to == Constants.MSG_SENDER) to = msg.sender;
else if (to == Constants.ADDRESS_THIS) to = address(this);
uint256 balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
_swapV2(path, to);
amountOut = IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore);
require(amountOut >= amountOutMin, 'Too little received');
}
/// @inheritdoc IV2SwapRouter
function swapTokensForExactTokensV2(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to
) external payable override returns (uint256 amountIn) {
amountIn = PulseXLibraryV2.getAmountsIn(factoryV2, amountOut, path)[0];
require(amountIn <= amountInMax, 'Too much requested');
pay(path[0], msg.sender, PulseXLibraryV2.pairFor(factoryV2, path[0], path[1]), amountIn);
// find and replace to addresses
if (to == Constants.MSG_SENDER) to = msg.sender;
else if (to == Constants.ADDRESS_THIS) to = address(this);
_swapV2(path, to);
}
}
contracts/interfaces/IMulticall.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
/// @title Multicall interface
/// @notice Enables calling multiple methods in a single call to the contract
interface IMulticall {
/// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
/// @dev The `msg.value` should not be trusted for any method callable from multicall.
/// @param data The encoded function data for each of the calls to make to this contract
/// @return results The results from each of the calls passed in via data
function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
}
@openzeppelin/contracts/utils/ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
contracts/libraries/StableSwapLibrary.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import '../interfaces/IStableSwap.sol';
import '../interfaces/IStableSwapInfo.sol';
import './LowGasSafeMath.sol';
library StableSwapLibrary {
using LowGasSafeMath for uint256;
// get the pool info in stable swap
function getStableInfo(
address swapContract,
address input,
address output
) internal view returns (uint256 i, uint256 j) {
require(swapContract != address(0), "getStableInfo: invalid pool address");
IStableSwap swap = IStableSwap(swapContract);
uint256 coins = swap.N_COINS();
if (coins == 2) {
address t0 = swap.coins(0);
i = input == t0 ? 0 : 1;
j = (i == 0) ? 1 : 0;
} else if (coins == 3) {
address t0 = swap.coins(0);
address t1 = swap.coins(1);
address t2 = swap.coins(2);
if (input == t0) i = 0;
else if (input == t1) i = 1;
else if (input == t2) i = 2;
if (output == t0) j = 0;
else if (output == t1) j = 1;
else if (output == t2) j = 2;
}
}
function getStableAmountsIn(
address swapContract,
address stableSwapInfo,
address[] memory path,
uint256 amountOut
) internal view returns (uint256[] memory amounts) {
uint256 length = path.length;
require(length >= 2, "getStableAmountsIn: incorrect length");
amounts = new uint256[](length);
amounts[length - 1] = amountOut;
for (uint256 i = length - 1; i > 0; i--) {
uint256 last = i - 1;
(uint256 k, uint256 j) = getStableInfo(swapContract, path[last], path[i]);
amounts[last] = IStableSwapInfo(stableSwapInfo).get_dx(swapContract, k, j, amounts[i], type(uint256).max);
}
}
}
contracts/interfaces/IV1SwapRouter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via PulseX V1
interface IV1SwapRouter {
/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance,
/// and swap the entire amount, enabling contracts to send tokens before calling this function.
/// @param amountIn The amount of token to swap
/// @param amountOutMin The minimum amount of output that must be received
/// @param path The ordered list of tokens to swap through
/// @param to The recipient address
/// @return amountOut The amount of the received token
function swapExactTokensForTokensV1(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to
) external payable returns (uint256 amountOut);
/// @notice Swaps as little as possible of one token for an exact amount of another token
/// @param amountOut The amount of token to swap for
/// @param amountInMax The maximum amount of input that the caller will pay
/// @param path The ordered list of tokens to swap through
/// @param to The recipient address
/// @return amountIn The amount of token to pay
function swapTokensForExactTokensV1(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to
) external payable returns (uint256 amountIn);
}
contracts/interfaces/IWETH9.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
/// @title Interface for WETH9
interface IWETH9 is IERC20 {
/// @notice Deposit ether to get wrapped ether
function deposit() external payable;
/// @notice Withdraw wrapped ether to get ether
function withdraw(uint256) external;
}
contracts/base/PeripheryValidationExtended.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
import './PeripheryValidation.sol';
abstract contract PeripheryValidationExtended is PeripheryValidation {
modifier checkPreviousBlockhash(bytes32 previousBlockhash) {
require(blockhash(block.number - 1) == previousBlockhash, 'Blockhash');
_;
}
}
contracts/base/MulticallExtended.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import './Multicall.sol';
import '../interfaces/IMulticallExtended.sol';
import '../base/PeripheryValidationExtended.sol';
/// @title Multicall
/// @notice Enables calling multiple methods in a single call to the contract
abstract contract MulticallExtended is IMulticallExtended, Multicall, PeripheryValidationExtended {
/// @inheritdoc IMulticallExtended
function multicall(uint256 deadline, bytes[] calldata data)
external
payable
override
checkDeadline(deadline)
returns (bytes[] memory)
{
return multicall(data);
}
/// @inheritdoc IMulticallExtended
function multicall(bytes32 previousBlockhash, bytes[] calldata data)
external
payable
override
checkPreviousBlockhash(previousBlockhash)
returns (bytes[] memory)
{
return multicall(data);
}
}
contracts/interfaces/IPeripheryPayments.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
/// @title Periphery Payments
/// @notice Functions to ease deposits and withdrawals of ETH
interface IPeripheryPayments {
/// @notice Unwraps the contract's WETH9 balance and sends it to recipient as ETH.
/// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users.
/// @param amountMinimum The minimum amount of WETH9 to unwrap
/// @param recipient The address receiving ETH
function unwrapWETH9(uint256 amountMinimum, address recipient) external payable;
/// @notice Refunds any ETH balance held by this contract to the `msg.sender`
/// @dev Useful for bundling with mint or increase liquidity that uses ether, or exact output swaps
/// that use ether for the input amount
function refundETH() external payable;
/// @notice Transfers the full amount of a token held by this contract to recipient
/// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users
/// @param token The contract address of the token which will be transferred to `recipient`
/// @param amountMinimum The minimum amount of token required for a transfer
/// @param recipient The destination address of the token
function sweepToken(
address token,
uint256 amountMinimum,
address recipient
) external payable;
}
contracts/libraries/LowGasSafeMath.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.0;
/// @title Optimized overflow and underflow safe math operations
/// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost
library LowGasSafeMath {
/// @notice Returns x + y, reverts if sum overflows uint256
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x);
}
/// @notice Returns x - y, reverts if underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x);
}
/// @notice Returns x * y, reverts if overflows
/// @param x The multiplicand
/// @param y The multiplier
/// @return z The product of x and y
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(x == 0 || (z = x * y) / x == y);
}
/// @notice Returns x + y, reverts if overflows or underflows
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x + y) >= x == (y >= 0));
}
/// @notice Returns x - y, reverts if overflows or underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x - y) <= x == (y >= 0));
}
}
contracts/interfaces/IImmutableState.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Immutable state
/// @notice Functions that return immutable state of the router
interface IImmutableState {
/// @return Returns the address of the PulseX V1 factory
function factoryV1() external view returns (address);
/// @return Returns the address of the PulseX V2 factory
function factoryV2() external view returns (address);
/// @return Returns the address of WETH9
function WETH9() external view returns (address);
}
contracts/V1SwapRouter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import './libraries/LowGasSafeMath.sol';
import './interfaces/IV1SwapRouter.sol';
import './base/ImmutableState.sol';
import './base/PeripheryPaymentsWithFeeExtended.sol';
import './libraries/Constants.sol';
import './libraries/PulseXLibraryV1.sol';
/// @title PulseX V1 Swap Router
/// @notice Router for stateless execution of swaps against PulseX V1
abstract contract V1SwapRouter is IV1SwapRouter, ImmutableState, PeripheryPaymentsWithFeeExtended {
using LowGasSafeMath for uint256;
// supports fee-on-transfer tokens
// requires the initial amount to have already been sent to the first pair
function _swapV1(address[] memory path, address _to) private {
for (uint256 i; i < path.length - 1; i++) {
(address input, address output) = (path[i], path[i + 1]);
(address token0, ) = PulseXLibraryV1.sortTokens(input, output);
IPulseXPair pair = IPulseXPair(PulseXLibraryV1.pairFor(factoryV1, 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 = PulseXLibraryV1.getAmountOut(amountInput, reserveInput, reserveOutput);
}
(uint256 amount0Out, uint256 amount1Out) =
input == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0));
address to = i < path.length - 2 ? PulseXLibraryV1.pairFor(factoryV1, output, path[i + 2]) : _to;
pair.swap(amount0Out, amount1Out, to, new bytes(0));
}
}
/// @inheritdoc IV1SwapRouter
function swapExactTokensForTokensV1(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to
) external payable override returns (uint256 amountOut) {
// use amountIn == Constants.CONTRACT_BALANCE as a flag to swap the entire balance of the contract
bool hasAlreadyPaid;
if (amountIn == Constants.CONTRACT_BALANCE) {
hasAlreadyPaid = true;
amountIn = IERC20(path[0]).balanceOf(address(this));
}
pay(
path[0],
hasAlreadyPaid ? address(this) : msg.sender,
PulseXLibraryV1.pairFor(factoryV1, path[0], path[1]),
amountIn
);
// find and replace to addresses
if (to == Constants.MSG_SENDER) to = msg.sender;
else if (to == Constants.ADDRESS_THIS) to = address(this);
uint256 balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
_swapV1(path, to);
amountOut = IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore);
require(amountOut >= amountOutMin, 'Too little received');
}
/// @inheritdoc IV1SwapRouter
function swapTokensForExactTokensV1(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to
) external payable override returns (uint256 amountIn) {
amountIn = PulseXLibraryV1.getAmountsIn(factoryV1, amountOut, path)[0];
require(amountIn <= amountInMax, 'Too much requested');
pay(path[0], msg.sender, PulseXLibraryV1.pairFor(factoryV1, path[0], path[1]), amountIn);
// find and replace to addresses
if (to == Constants.MSG_SENDER) to = msg.sender;
else if (to == Constants.ADDRESS_THIS) to = address(this);
_swapV1(path, to);
}
}
contracts/base/PeripheryPayments.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '../interfaces/IPeripheryPayments.sol';
import '../interfaces/IWETH9.sol';
import '../libraries/TransferHelper.sol';
import './ImmutableState.sol';
abstract contract PeripheryPayments is IPeripheryPayments, ImmutableState {
receive() external payable {
require(msg.sender == WETH9, 'Not WETH9');
}
/// @inheritdoc IPeripheryPayments
function unwrapWETH9(uint256 amountMinimum, address recipient) public payable override {
uint256 balanceWETH9 = IWETH9(WETH9).balanceOf(address(this));
require(balanceWETH9 >= amountMinimum, 'Insufficient WETH9');
if (balanceWETH9 > 0) {
IWETH9(WETH9).withdraw(balanceWETH9);
TransferHelper.safeTransferETH(recipient, balanceWETH9);
}
}
/// @inheritdoc IPeripheryPayments
function sweepToken(
address token,
uint256 amountMinimum,
address recipient
) public payable override {
uint256 balanceToken = IERC20(token).balanceOf(address(this));
require(balanceToken >= amountMinimum, 'Insufficient token');
if (balanceToken > 0) {
TransferHelper.safeTransfer(token, recipient, balanceToken);
}
}
/// @inheritdoc IPeripheryPayments
function refundETH() external payable override {
if (address(this).balance > 0) TransferHelper.safeTransferETH(msg.sender, address(this).balance);
}
/// @param token The token to pay
/// @param payer The entity that must pay
/// @param recipient The entity that will receive payment
/// @param value The amount to pay
function pay(
address token,
address payer,
address recipient,
uint256 value
) internal {
if (token == WETH9 && address(this).balance >= value) {
// pay with WETH9
IWETH9(WETH9).deposit{value: value}(); // wrap only what is needed to pay
IWETH9(WETH9).transfer(recipient, value);
} else if (payer == address(this)) {
// pay with tokens already in the contract (for the exact input multihop case)
TransferHelper.safeTransfer(token, recipient, value);
} else {
// pull payment
TransferHelper.safeTransferFrom(token, payer, recipient, value);
}
}
}
contracts/interfaces/IStableSwapRouter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via PulseX Stable Swap
interface IStableSwapRouter {
function exactInputStableSwap(
address[] calldata path,
address[] calldata swapContracts,
uint256 amountIn,
uint256 amountOutMin,
address to
) external payable returns (uint256 amountOut);
function exactOutputStableSwap(
address[] calldata path,
address[] calldata swapContracts,
uint256 amountOut,
uint256 amountInMax,
address to
) external payable returns (uint256 amountIn);
}
contracts/interfaces/IStableSwap.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.7.6;
pragma abicoder v2;
interface IStableSwap {
// solium-disable-next-line mixedcase
function get_dy(
uint256 i,
uint256 j,
uint256 dx
) external view returns (uint256 dy);
// solium-disable-next-line mixedcase
function exchange(
uint256 i,
uint256 j,
uint256 dx,
uint256 minDy
) external payable;
// solium-disable-next-line mixedcase
function N_COINS() external view returns (uint256);
// solium-disable-next-line mixedcase
function coins(uint256 i) external view returns (address);
// solium-disable-next-line mixedcase
function balances(uint256 i) external view returns (uint256);
// solium-disable-next-line mixedcase
function A() external view returns (uint256);
// solium-disable-next-line mixedcase
function fee() external view returns (uint256);
}
@openzeppelin/contracts/drafts/IERC20Permit.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @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.
*/
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].
*/
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);
}
contracts/interfaces/IPulseXSwapRouter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
import './ISelfPermit.sol';
import './IV1SwapRouter.sol';
import './IV2SwapRouter.sol';
import './IStableSwapRouter.sol';
import './IMulticallExtended.sol';
/// @title Router token swapping functionality
interface IPulseXSwapRouter is IV1SwapRouter, IV2SwapRouter, IStableSwapRouter, IMulticallExtended, ISelfPermit {
}
contracts/base/PeripheryPaymentsWithFeeExtended.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
import './PeripheryPaymentsWithFee.sol';
import '../interfaces/IPeripheryPaymentsWithFeeExtended.sol';
import './PeripheryPaymentsExtended.sol';
abstract contract PeripheryPaymentsWithFeeExtended is
IPeripheryPaymentsWithFeeExtended,
PeripheryPaymentsExtended,
PeripheryPaymentsWithFee
{
/// @inheritdoc IPeripheryPaymentsWithFeeExtended
function unwrapWETH9WithFee(
uint256 amountMinimum,
uint256 feeBips,
address feeRecipient
) external payable override {
unwrapWETH9WithFee(amountMinimum, msg.sender, feeBips, feeRecipient);
}
/// @inheritdoc IPeripheryPaymentsWithFeeExtended
function sweepTokenWithFee(
address token,
uint256 amountMinimum,
uint256 feeBips,
address feeRecipient
) external payable override {
sweepTokenWithFee(token, amountMinimum, msg.sender, feeBips, feeRecipient);
}
}
contracts/interfaces/IERC20PermitAllowed.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Interface for permit
/// @notice Interface used by DAI/CHAI for permit
interface IERC20PermitAllowed {
/// @notice Approve the spender to spend some tokens via the holder signature
/// @dev This is the permit interface used by DAI and CHAI
/// @param holder The address of the token holder, the token owner
/// @param spender The address of the token spender
/// @param nonce The holder's nonce, increases at each call to permit
/// @param expiry The timestamp at which the permit is no longer valid
/// @param allowed Boolean that sets approval amount, true for type(uint256).max and false for 0
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function permit(
address holder,
address spender,
uint256 nonce,
uint256 expiry,
bool allowed,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
contracts/base/PeripheryValidation.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
import './BlockTimestamp.sol';
abstract contract PeripheryValidation is BlockTimestamp {
modifier checkDeadline(uint256 deadline) {
require(_blockTimestamp() <= deadline, 'Transaction too old');
_;
}
}
contracts/libraries/Constants.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
/// @title Constant state
/// @notice Constant state used by the swap router
library Constants {
/// @dev Used for identifying cases when this contract's balance of a token is to be used
uint256 internal constant CONTRACT_BALANCE = 0;
/// @dev Used as a flag for identifying msg.sender, saves gas by sending more 0 bytes
address internal constant MSG_SENDER = address(1);
/// @dev Used as a flag for identifying address(this), saves gas by sending more 0 bytes
address internal constant ADDRESS_THIS = address(2);
}
@openzeppelin/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @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);
}
contracts/base/PeripheryPaymentsWithFee.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '../libraries/LowGasSafeMath.sol';
import './PeripheryPayments.sol';
import '../interfaces/IPeripheryPaymentsWithFee.sol';
import '../interfaces/IWETH9.sol';
import '../libraries/TransferHelper.sol';
abstract contract PeripheryPaymentsWithFee is PeripheryPayments, IPeripheryPaymentsWithFee {
using LowGasSafeMath for uint256;
/// @inheritdoc IPeripheryPaymentsWithFee
function unwrapWETH9WithFee(
uint256 amountMinimum,
address recipient,
uint256 feeBips,
address feeRecipient
) public payable override {
require(feeBips > 0 && feeBips <= 100);
uint256 balanceWETH9 = IWETH9(WETH9).balanceOf(address(this));
require(balanceWETH9 >= amountMinimum, 'Insufficient WETH9');
if (balanceWETH9 > 0) {
IWETH9(WETH9).withdraw(balanceWETH9);
uint256 feeAmount = balanceWETH9.mul(feeBips) / 10_000;
if (feeAmount > 0) TransferHelper.safeTransferETH(feeRecipient, feeAmount);
TransferHelper.safeTransferETH(recipient, balanceWETH9 - feeAmount);
}
}
/// @inheritdoc IPeripheryPaymentsWithFee
function sweepTokenWithFee(
address token,
uint256 amountMinimum,
address recipient,
uint256 feeBips,
address feeRecipient
) public payable override {
require(feeBips > 0 && feeBips <= 100);
uint256 balanceToken = IERC20(token).balanceOf(address(this));
require(balanceToken >= amountMinimum, 'Insufficient token');
if (balanceToken > 0) {
uint256 feeAmount = balanceToken.mul(feeBips) / 10_000;
if (feeAmount > 0) TransferHelper.safeTransfer(token, feeRecipient, feeAmount);
TransferHelper.safeTransfer(token, recipient, balanceToken - feeAmount);
}
}
}
Compiler Settings
{"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"],"":["ast"]}},"optimizer":{"runs":1000000,"enabled":true},"metadata":{"bytecodeHash":"none"},"libraries":{},"evmVersion":"istanbul"}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_factoryV1","internalType":"address"},{"type":"address","name":"factoryV2","internalType":"address"},{"type":"address","name":"_stableInfo","internalType":"address"},{"type":"address","name":"_WETH9","internalType":"address"}]},{"type":"event","name":"SetStableSwap","inputs":[{"type":"address","name":"factory","internalType":"address","indexed":true},{"type":"address","name":"info","internalType":"address","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"WETH9","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"exactInputStableSwap","inputs":[{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address[]","name":"swapContracts","internalType":"address[]"},{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"}],"name":"exactOutputStableSwap","inputs":[{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address[]","name":"swapContracts","internalType":"address[]"},{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"uint256","name":"amountInMax","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"factoryV1","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"factoryV2","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[{"type":"bytes[]","name":"","internalType":"bytes[]"}],"name":"multicall","inputs":[{"type":"bytes32","name":"previousBlockhash","internalType":"bytes32"},{"type":"bytes[]","name":"data","internalType":"bytes[]"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"bytes[]","name":"","internalType":"bytes[]"}],"name":"multicall","inputs":[{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"bytes[]","name":"data","internalType":"bytes[]"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"bytes[]","name":"results","internalType":"bytes[]"}],"name":"multicall","inputs":[{"type":"bytes[]","name":"data","internalType":"bytes[]"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"pull","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"refundETH","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"selfPermit","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"selfPermitAllowed","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"uint256","name":"expiry","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"selfPermitAllowedIfNecessary","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"uint256","name":"expiry","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"selfPermitIfNecessary","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"stableSwapInfo","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"swapExactTokensForTokensV1","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":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"swapExactTokensForTokensV2","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":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"}],"name":"swapTokensForExactTokensV1","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":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"}],"name":"swapTokensForExactTokensV2","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":"function","stateMutability":"payable","outputs":[],"name":"sweepToken","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amountMinimum","internalType":"uint256"},{"type":"address","name":"recipient","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"sweepToken","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amountMinimum","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"sweepTokenWithFee","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amountMinimum","internalType":"uint256"},{"type":"uint256","name":"feeBips","internalType":"uint256"},{"type":"address","name":"feeRecipient","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"sweepTokenWithFee","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amountMinimum","internalType":"uint256"},{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"feeBips","internalType":"uint256"},{"type":"address","name":"feeRecipient","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"unwrapWETH9","inputs":[{"type":"uint256","name":"amountMinimum","internalType":"uint256"},{"type":"address","name":"recipient","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"unwrapWETH9","inputs":[{"type":"uint256","name":"amountMinimum","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"unwrapWETH9WithFee","inputs":[{"type":"uint256","name":"amountMinimum","internalType":"uint256"},{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"feeBips","internalType":"uint256"},{"type":"address","name":"feeRecipient","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"unwrapWETH9WithFee","inputs":[{"type":"uint256","name":"amountMinimum","internalType":"uint256"},{"type":"uint256","name":"feeBips","internalType":"uint256"},{"type":"address","name":"feeRecipient","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"wrapETH","inputs":[{"type":"uint256","name":"value","internalType":"uint256"}]},{"type":"receive","stateMutability":"payable"}]
Contract Creation Code
0x60e06040523480156200001157600080fd5b5060405162004b4038038062004b4083398101604081905262000034916200009e565b606093841b6001600160601b031990811660805292841b831660a05290921b1660c0526001600081905580546001600160a01b0319166001600160a01b03909216919091179055620000fa565b80516001600160a01b03811681146200009957600080fd5b919050565b60008060008060808587031215620000b4578384fd5b620000bf8562000081565b9350620000cf6020860162000081565b9250620000df6040860162000081565b9150620000ef6060860162000081565b905092959194509250565b60805160601c60a05160601c60c05160601c6149b16200018f600039806101d852806104c052806108aa52806109d45280610a7b5280610ea05280610fca52806124d1528061253152806125b2525080610e60528061160e5280611c775280611d4b5280612cd95280612e9c525080610549528061062652806107c45280611185528061270452806128c752506149b16000f3fe6080604052600436106101bb5760003560e01c80639b5c7a81116100ec578063cafe95a01161008a578063e0e189a011610064578063e0e189a014610460578063e90a182f14610473578063f2d5d56b14610486578063f3995c671461049957610266565b8063cafe95a014610427578063d4ef38de1461043a578063df2ab5bb1461044d57610266565b8063ac9650d8116100c6578063ac9650d8146103d9578063b85aa7af146103ec578063c2e3140a14610401578063c8bb18561461041457610266565b80639b5c7a81146103a0578063a4a78f0c146103b3578063ab0acea4146103c657610266565b806349404b7c116101595780634aa94288116101335780634aa94288146103525780635ae401dc1461036557806368e0d4e1146103785780639b2c0a371461038d57610266565b806349404b7c14610317578063496169971461032a5780634aa4a4fc1461033d57610266565b80631f0464d1116101955780631f0464d1146102af5780633068c554146102cf5780633957f453146102e25780634659a4941461030457610266565b806312210e8a1461026b5780631c58db4f146102735780631d4389f51461028657610266565b36610266573373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461026457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f4e6f742057455448390000000000000000000000000000000000000000000000604482015290519081900360640190fd5b005b600080fd5b6102646104ac565b610264610281366004614500565b6104be565b6102996102943660046145ca565b610542565b6040516102a69190614759565b60405180910390f35b6102c26102bd3660046143c8565b610725565b6040516102a691906146db565b6102646102dd36600461424b565b6107af565b3480156102ee57600080fd5b506102f76107c2565b6040516102a6919061467f565b610264610312366004614294565b6107e6565b610264610325366004614530565b6108a6565b610264610338366004614500565b610a6c565b34801561034957600080fd5b506102f7610a79565b6102996103603660046142f4565b610a9d565b6102c26103733660046143c8565b610de5565b34801561038457600080fd5b506102f7610e5e565b61026461039b36600461455f565b610e82565b6102996103ae3660046145ca565b611098565b6102646103c1366004614294565b61144c565b6102996103d43660046145ca565b611521565b6102c26103e7366004614388565b6117cd565b3480156103f857600080fd5b506102f7611927565b61026461040f366004614294565b611943565b6102996104223660046142f4565b6119f8565b6102996104353660046145ca565b611c70565b61026461044836600461459d565b611e21565b61026461045b3660046141ac565b611e2d565b61026461046e3660046141ed565b611f44565b610264610481366004614181565b6120aa565b610264610494366004614181565b6120b9565b6102646104a7366004614294565b6120c5565b47156104bc576104bc334761215d565b565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561052657600080fd5b505af115801561053a573d6000803e3d6000fd5b505050505050565b60006105a27f0000000000000000000000000000000000000000000000000000000000000000878686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506122ab92505050565b6000815181106105ae57fe5b60200260200101519050848111156105fb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f29061482f565b60405180910390fd5b6106948484600081811061060b57fe5b90506020020160208101906106209190614142565b3361068e7f00000000000000000000000000000000000000000000000000000000000000008888600081811061065257fe5b90506020020160208101906106679190614142565b8989600181811061067457fe5b90506020020160208101906106899190614142565b6123e4565b846124cf565b73ffffffffffffffffffffffffffffffffffffffff8216600114156106bb573391506106de565b73ffffffffffffffffffffffffffffffffffffffff8216600214156106de573091505b61071c8484808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508692506126ad915050565b95945050505050565b6060838060014303401461079a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f426c6f636b686173680000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6107a484846117cd565b91505b509392505050565b6107bc8484338585611f44565b50505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b604080517f8fcbaf0c00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101879052606481018690526001608482015260ff851660a482015260c4810184905260e48101839052905173ffffffffffffffffffffffffffffffffffffffff881691638fcbaf0c9161010480830192600092919082900301818387803b15801561088657600080fd5b505af115801561089a573d6000803e3d6000fd5b50505050505050505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561092f57600080fd5b505afa158015610943573d6000803e3d6000fd5b505050506040513d602081101561095957600080fd5b50519050828110156109cc57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e73756666696369656e742057455448390000000000000000000000000000604482015290519081900360640190fd5b8015610a67577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015610a4557600080fd5b505af1158015610a59573d6000803e3d6000fd5b50505050610a67828261215d565b505050565b610a7681336108a6565b50565b7f000000000000000000000000000000000000000000000000000000000000000081565b600060026000541415610b1157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600090815588888281610b2257fe5b9050602002016020810190610b379190614142565b9050600089897fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101818110610b6957fe5b9050602002016020810190610b7e9190614142565b9050600086610c3057506040517f70a0823100000000000000000000000000000000000000000000000000000000815260019073ffffffffffffffffffffffffffffffffffffffff8416906370a0823190610bdd90309060040161467f565b60206040518083038186803b158015610bf557600080fd5b505afa158015610c09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2d9190614518565b96505b80610c4157610c418333308a6124cf565b610cae8b8b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808f0282810182019093528e82529093508e92508d9182918501908490808284376000920191909152506129b292505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316906370a0823190610d0090309060040161467f565b60206040518083038186803b158015610d1857600080fd5b505afa158015610d2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d509190614518565b935085841015610d5f57600080fd5b73ffffffffffffffffffffffffffffffffffffffff851660011415610d8657339450610da9565b73ffffffffffffffffffffffffffffffffffffffff851660021415610da9573094505b73ffffffffffffffffffffffffffffffffffffffff85163014610dd257610dd2823087876124cf565b5050600160005550979650505050505050565b60608380610df1612b59565b111561079a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f5472616e73616374696f6e20746f6f206f6c6400000000000000000000000000604482015290519081900360640190fd5b7f000000000000000000000000000000000000000000000000000000000000000081565b600082118015610e93575060648211155b610e9c57600080fd5b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015610f2557600080fd5b505afa158015610f39573d6000803e3d6000fd5b505050506040513d6020811015610f4f57600080fd5b5051905084811015610fc257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e73756666696369656e742057455448390000000000000000000000000000604482015290519081900360640190fd5b8015611091577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561103b57600080fd5b505af115801561104f573d6000803e3d6000fd5b50505050600061271061106b8584612b5d90919063ffffffff16565b8161107257fe5b049050801561108557611085838261215d565b61053a8582840361215d565b5050505050565b6000808661114e5750600184846000816110ae57fe5b90506020020160208101906110c39190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016110fb919061467f565b60206040518083038186803b15801561111357600080fd5b505afa158015611127573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114b9190614518565b96505b6111d98585600081811061115e57fe5b90506020020160208101906111739190614142565b8261117e5733611180565b305b6111d37f0000000000000000000000000000000000000000000000000000000000000000898960008181106111b157fe5b90506020020160208101906111c69190614142565b8a8a600181811061067457fe5b8a6124cf565b73ffffffffffffffffffffffffffffffffffffffff83166001141561120057339250611223565b73ffffffffffffffffffffffffffffffffffffffff831660021415611223573092505b600085857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810181811061125357fe5b90506020020160208101906112689190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231856040518263ffffffff1660e01b81526004016112a0919061467f565b60206040518083038186803b1580156112b857600080fd5b505afa1580156112cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112f09190614518565b90506113308686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508892506126ad915050565b6114058187877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810181811061136257fe5b90506020020160208101906113779190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231876040518263ffffffff1660e01b81526004016113af919061467f565b60206040518083038186803b1580156113c757600080fd5b505afa1580156113db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ff9190614518565b90612b87565b925086831015611441576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f290614866565b505095945050505050565b604080517fdd62ed3e00000000000000000000000000000000000000000000000000000000815233600482015230602482015290517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9173ffffffffffffffffffffffffffffffffffffffff89169163dd62ed3e91604480820192602092909190829003018186803b1580156114e157600080fd5b505afa1580156114f5573d6000803e3d6000fd5b505050506040513d602081101561150b57600080fd5b5051101561053a5761053a8686868686866107e6565b600080866115d757506001848460008161153757fe5b905060200201602081019061154c9190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401611584919061467f565b60206040518083038186803b15801561159c57600080fd5b505afa1580156115b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115d49190614518565b96505b611676858560008181106115e757fe5b90506020020160208101906115fc9190614142565b826116075733611609565b305b6111d37f00000000000000000000000000000000000000000000000000000000000000008989600081811061163a57fe5b905060200201602081019061164f9190614142565b8a8a600181811061165c57fe5b90506020020160208101906116719190614142565b612b97565b73ffffffffffffffffffffffffffffffffffffffff83166001141561169d573392506116c0565b73ffffffffffffffffffffffffffffffffffffffff8316600214156116c0573092505b600085857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018181106116f057fe5b90506020020160208101906117059190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231856040518263ffffffff1660e01b815260040161173d919061467f565b60206040518083038186803b15801561175557600080fd5b505afa158015611769573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178d9190614518565b9050611330868680806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250889250612c82915050565b60608167ffffffffffffffff811180156117e657600080fd5b5060405190808252806020026020018201604052801561181a57816020015b60608152602001906001900390816118055790505b50905060005b82811015611920576000803086868581811061183857fe5b905060200281019061184a91906148f3565b60405161185892919061466f565b600060405180830381855af49150503d8060008114611893576040519150601f19603f3d011682016040523d82523d6000602084013e611898565b606091505b5091509150816118fe576044815110156118b157600080fd5b600481019050808060200190518101906118cb9190614412565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f29190614762565b8084848151811061190b57fe5b60209081029190910101525050600101611820565b5092915050565b60015473ffffffffffffffffffffffffffffffffffffffff1681565b604080517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523360048201523060248201529051869173ffffffffffffffffffffffffffffffffffffffff89169163dd62ed3e91604480820192602092909190829003018186803b1580156119b857600080fd5b505afa1580156119cc573d6000803e3d6000fd5b505050506040513d60208110156119e257600080fd5b5051101561053a5761053a8686868686866120c5565b600060026000541415611a6c57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600081905550611aee86866000818110611a8457fe5b9050602002016020810190611a999190614142565b600154604080516020808d0282810182019093528c825273ffffffffffffffffffffffffffffffffffffffff9093169290918d918d9182918501908490808284376000920191909152508a9250612f87915050565b600081518110611afa57fe5b6020026020010151905082811115611b1157600080fd5b611b3e88886000818110611b2157fe5b9050602002016020810190611b369190614142565b3330846124cf565b611bab88888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808c0282810182019093528b82529093508b92508a9182918501908490808284376000920191909152506129b292505050565b73ffffffffffffffffffffffffffffffffffffffff821660011415611bd257339150611bf5565b73ffffffffffffffffffffffffffffffffffffffff821660021415611bf5573091505b73ffffffffffffffffffffffffffffffffffffffff82163014611c6057611c6088887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101818110611c4357fe5b9050602002016020810190611c589190614142565b3084876124cf565b6001600055979650505050505050565b6000611cd07f00000000000000000000000000000000000000000000000000000000000000008786868080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061319f92505050565b600081518110611cdc57fe5b6020026020010151905084811115611d20576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f29061482f565b611d9984846000818110611d3057fe5b9050602002016020810190611d459190614142565b3361068e7f000000000000000000000000000000000000000000000000000000000000000088886000818110611d7757fe5b9050602002016020810190611d8c9190614142565b8989600181811061165c57fe5b73ffffffffffffffffffffffffffffffffffffffff821660011415611dc057339150611de3565b73ffffffffffffffffffffffffffffffffffffffff821660021415611de3573091505b61071c848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250869250612c82915050565b610a6783338484610e82565b60008373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611e9657600080fd5b505afa158015611eaa573d6000803e3d6000fd5b505050506040513d6020811015611ec057600080fd5b5051905082811015611f3357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e73756666696369656e7420746f6b656e0000000000000000000000000000604482015290519081900360640190fd5b80156107bc576107bc8483836132c9565b600082118015611f55575060648211155b611f5e57600080fd5b60008573ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611fc757600080fd5b505afa158015611fdb573d6000803e3d6000fd5b505050506040513d6020811015611ff157600080fd5b505190508481101561206457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e73756666696369656e7420746f6b656e0000000000000000000000000000604482015290519081900360640190fd5b801561053a5760006127106120798386612b5d565b8161208057fe5b0490508015612094576120948784836132c9565b6120a187868385036132c9565b50505050505050565b6120b5828233611e2d565b5050565b6120b58233308461349e565b604080517fd505accf000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018790526064810186905260ff8516608482015260a4810184905260c48101839052905173ffffffffffffffffffffffffffffffffffffffff88169163d505accf9160e480830192600092919082900301818387803b15801561088657600080fd5b6040805160008082526020820190925273ffffffffffffffffffffffffffffffffffffffff84169083906040518082805190602001908083835b602083106121d457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101612197565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114612236576040519150601f19603f3d011682016040523d82523d6000602084013e61223b565b606091505b5050905080610a6757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f5354450000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60606002825110156122bc57600080fd5b815167ffffffffffffffff811180156122d457600080fd5b506040519080825280602002602001820160405280156122fe578160200160208202803683370190505b509050828160018351038151811061231257fe5b602090810291909101015281517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff015b80156107a75760008061237f8786600186038151811061235e57fe5b602002602001015187868151811061237257fe5b602002602001015161367b565b915091506123a184848151811061239257fe5b60200260200101518383613763565b8460018503815181106123b057fe5b602090810291909101015250507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01612342565b60008060006123f38585613839565b604080517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501207fff0000000000000000000000000000000000000000000000000000000000000060688401529a90941b9093166069840152607d8301989098527f59fffffddd756cba9095128e53f3291a6ba38b21e3df744936e7289326555d62609d808401919091528851808403909101815260bd909201909752805196019590952095945050505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614801561252a5750804710155b15612673577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561259757600080fd5b505af11580156125ab573d6000803e3d6000fd5b50505050507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb83836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15801561264157600080fd5b505af1158015612655573d6000803e3d6000fd5b505050506040513d602081101561266b57600080fd5b506107bc9050565b73ffffffffffffffffffffffffffffffffffffffff83163014156126a15761269c8483836132c9565b6107bc565b6107bc8484848461349e565b60005b6001835103811015610a67576000808483815181106126cb57fe5b60200260200101518584600101815181106126e257fe5b60200260200101519150915060006126fa8383613839565b509050600061272a7f000000000000000000000000000000000000000000000000000000000000000085856123e4565b90506000806000808473ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561277857600080fd5b505afa15801561278c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127b091906144bd565b506dffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff1691506000808773ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff1614612812578284612815565b83835b91509150612856828b73ffffffffffffffffffffffffffffffffffffffff166370a082318a6040518263ffffffff1660e01b81526004016113af919061467f565b95506128638683836138de565b9450505050506000808573ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16146128a7578260006128ab565b6000835b91509150600060028c51038a106128c2578a612903565b6129037f0000000000000000000000000000000000000000000000000000000000000000898e8d600201815181106128f657fe5b60200260200101516123e4565b604080516000815260208101918290527f022c0d9f0000000000000000000000000000000000000000000000000000000090915290915073ffffffffffffffffffffffffffffffffffffffff87169063022c0d9f9061296b908690869086906024810161489d565b600060405180830381600087803b15801561298557600080fd5b505af1158015612999573d6000803e3d6000fd5b50506001909b019a506126b09950505050505050505050565b60005b6001835103811015610a67576000808483815181106129d057fe5b60200260200101518584600101815181106129e757fe5b602002602001015191509150600080612a14868681518110612a0557fe5b602002602001015185856139b4565b9150915060008473ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401612a53919061467f565b60206040518083038186803b158015612a6b57600080fd5b505afa158015612a7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aa39190614518565b9050612ac385888881518110612ab557fe5b602002602001015183613ed8565b868681518110612acf57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16635b41b90884848460006040518563ffffffff1660e01b8152600401612b1694939291906148d8565b600060405180830381600087803b158015612b3057600080fd5b505af1158015612b44573d6000803e3d6000fd5b5050600190970196506129b595505050505050565b4290565b6000821580612b7857505081810281838281612b7557fe5b04145b612b8157600080fd5b92915050565b80820382811115612b8157600080fd5b6000806000612ba68585613839565b604080517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501207fff0000000000000000000000000000000000000000000000000000000000000060688401529a90941b9093166069840152607d8301989098527f5dff1ac2d132f5ac2841294c6e9fc0ebafae8d447fac7996ef21c21112f411f1609d808401919091528851808403909101815260bd909201909752805196019590952095945050505050565b60005b6001835103811015610a6757600080848381518110612ca057fe5b6020026020010151858460010181518110612cb757fe5b6020026020010151915091506000612ccf8383613839565b5090506000612cff7f00000000000000000000000000000000000000000000000000000000000000008585612b97565b90506000806000808473ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b158015612d4d57600080fd5b505afa158015612d61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d8591906144bd565b506dffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff1691506000808773ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff1614612de7578284612dea565b83835b91509150612e2b828b73ffffffffffffffffffffffffffffffffffffffff166370a082318a6040518263ffffffff1660e01b81526004016113af919061467f565b9550612e388683836138de565b9450505050506000808573ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614612e7c57826000612e80565b6000835b91509150600060028c51038a10612e97578a612ed8565b612ed87f0000000000000000000000000000000000000000000000000000000000000000898e8d60020181518110612ecb57fe5b6020026020010151612b97565b604080516000815260208101918290527f022c0d9f0000000000000000000000000000000000000000000000000000000090915290915073ffffffffffffffffffffffffffffffffffffffff87169063022c0d9f90612f40908690869086906024810161489d565b600060405180830381600087803b158015612f5a57600080fd5b505af1158015612f6e573d6000803e3d6000fd5b50506001909b019a50612c859950505050505050505050565b81516060906002811015612fc7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f290614775565b8067ffffffffffffffff81118015612fde57600080fd5b50604051908082528060200260200182016040528015613008578160200160208202803683370190505b5091508282600183038151811061301b57fe5b60209081029190910101527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81015b801561319557600060018203905060008061308c8a89858151811061306b57fe5b60200260200101518a878151811061307f57fe5b60200260200101516139b4565b915091508873ffffffffffffffffffffffffffffffffffffffff1663ca4bc7148b84848a89815181106130bb57fe5b60200260200101517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518663ffffffff1660e01b81526004016131049594939291906146a0565b60206040518083038186803b15801561311c57600080fd5b505afa158015613130573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131549190614518565b86848151811061316057fe5b60209081029190910101525050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0161304a565b5050949350505050565b60606002825110156131b057600080fd5b815167ffffffffffffffff811180156131c857600080fd5b506040519080825280602002602001820160405280156131f2578160200160208202803683370190505b509050828160018351038151811061320657fe5b602090810291909101015281517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff015b80156107a7576000806132738786600186038151811061325257fe5b602002602001015187868151811061326657fe5b60200260200101516140ad565b9150915061328684848151811061239257fe5b84600185038151811061329557fe5b602090810291909101015250507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01613236565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001781529251825160009485949389169392918291908083835b6020831061339e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613361565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613400576040519150601f19603f3d011682016040523d82523d6000602084013e613405565b606091505b5091509150818015613433575080511580613433575080806020019051602081101561343057600080fd5b50515b61109157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f5354000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6040805173ffffffffffffffffffffffffffffffffffffffff85811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd00000000000000000000000000000000000000000000000000000000178152925182516000948594938a169392918291908083835b6020831061357b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161353e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146135dd576040519150601f19603f3d011682016040523d82523d6000602084013e6135e2565b606091505b5091509150818015613610575080511580613610575080806020019051602081101561360d57600080fd5b50515b61053a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f5354460000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600080600061368a8585613839565b50905060008061369b8888886123e4565b73ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b1580156136e057600080fd5b505afa1580156136f4573d6000803e3d6000fd5b505050506040513d606081101561370a57600080fd5b5080516020909101516dffffffffffffffffffffffffffff918216935016905073ffffffffffffffffffffffffffffffffffffffff87811690841614613751578082613754565b81815b90999098509650505050505050565b60008084116137d357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f494e53554646494349454e545f4f55545055545f414d4f554e54000000000000604482015290519081900360640190fd5b6000831180156137e35750600082115b6137ec57600080fd5b60006138046127106137fe8688612b5d565b90612b5d565b905060006138186126f36137fe8689612b87565b905061382f600182848161382857fe5b04906140cd565b9695505050505050565b6000808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16141561387557600080fd5b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16106138af5782846138b2565b83835b909250905073ffffffffffffffffffffffffffffffffffffffff82166138d757600080fd5b9250929050565b600080841161394e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f494e53554646494349454e545f494e5055545f414d4f554e5400000000000000604482015290519081900360640190fd5b60008311801561395e5750600082115b61396757600080fd5b6000613975856126f3612b5d565b905060006139838285612b5d565b9050600061399d8361399788612710612b5d565b906140cd565b90508082816139a857fe5b04979650505050505050565b60008073ffffffffffffffffffffffffffffffffffffffff8516613a04576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f2906147d2565b600085905060008173ffffffffffffffffffffffffffffffffffffffff1663293577506040518163ffffffff1660e01b815260040160206040518083038186803b158015613a5157600080fd5b505afa158015613a65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a899190614518565b90508060021415613b98576040517fc661065700000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff84169063c661065790613ae9908490600401614759565b60206040518083038186803b158015613b0157600080fd5b505afa158015613b15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b399190614165565b90508073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614613b75576001613b78565b60005b60ff1694508415613b8a576000613b8d565b60015b60ff16935050613ece565b8060031415613ece576040517fc661065700000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff84169063c661065790613bf6908490600401614759565b60206040518083038186803b158015613c0e57600080fd5b505afa158015613c22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c469190614165565b905060008373ffffffffffffffffffffffffffffffffffffffff1663c661065760016040518263ffffffff1660e01b8152600401613c849190614759565b60206040518083038186803b158015613c9c57600080fd5b505afa158015613cb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cd49190614165565b905060008473ffffffffffffffffffffffffffffffffffffffff1663c661065760026040518263ffffffff1660e01b8152600401613d129190614759565b60206040518083038186803b158015613d2a57600080fd5b505afa158015613d3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d629190614165565b90508273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415613da15760009650613e17565b8173ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415613dde5760019650613e17565b8073ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415613e1757600296505b8273ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415613e545760009550613eca565b8173ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415613e915760019550613eca565b8073ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415613eca57600295505b5050505b5050935093915050565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b3000000000000000000000000000000000000000000000000000000001781529251825160009485949389169392918291908083835b60208310613fad57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613f70565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461400f576040519150601f19603f3d011682016040523d82523d6000602084013e614014565b606091505b5091509150818015614042575080511580614042575080806020019051602081101561403f57600080fd5b50515b61109157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f5341000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60008060006140bc8585613839565b50905060008061369b888888612b97565b80820182811015612b8157600080fd5b60008083601f8401126140ee578081fd5b50813567ffffffffffffffff811115614105578182fd5b60208301915083602080830285010111156138d757600080fd5b80516dffffffffffffffffffffffffffff8116811461413d57600080fd5b919050565b600060208284031215614153578081fd5b813561415e81614982565b9392505050565b600060208284031215614176578081fd5b815161415e81614982565b60008060408385031215614193578081fd5b823561419e81614982565b946020939093013593505050565b6000806000606084860312156141c0578081fd5b83356141cb81614982565b92506020840135915060408401356141e281614982565b809150509250925092565b600080600080600060a08688031215614204578081fd5b853561420f81614982565b945060208601359350604086013561422681614982565b925060608601359150608086013561423d81614982565b809150509295509295909350565b60008060008060808587031215614260578384fd5b843561426b81614982565b93506020850135925060408501359150606085013561428981614982565b939692955090935050565b60008060008060008060c087890312156142ac578081fd5b86356142b781614982565b95506020870135945060408701359350606087013560ff811681146142da578182fd5b9598949750929560808101359460a0909101359350915050565b600080600080600080600060a0888a03121561430e578081fd5b873567ffffffffffffffff80821115614325578283fd5b6143318b838c016140dd565b909950975060208a0135915080821115614349578283fd5b506143568a828b016140dd565b9096509450506040880135925060608801359150608088013561437881614982565b8091505092959891949750929550565b6000806020838503121561439a578081fd5b823567ffffffffffffffff8111156143b0578182fd5b6143bc858286016140dd565b90969095509350505050565b6000806000604084860312156143dc578081fd5b83359250602084013567ffffffffffffffff8111156143f9578182fd5b614405868287016140dd565b9497909650939450505050565b600060208284031215614423578081fd5b815167ffffffffffffffff8082111561443a578283fd5b818401915084601f83011261444d578283fd5b81518181111561445957fe5b60405160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116820101818110848211171561449557fe5b6040528181528382016020018710156144ac578485fd5b61382f826020830160208701614956565b6000806000606084860312156144d1578081fd5b6144da8461411f565b92506144e86020850161411f565b9150604084015163ffffffff811681146141e2578182fd5b600060208284031215614511578081fd5b5035919050565b600060208284031215614529578081fd5b5051919050565b60008060408385031215614542578182fd5b82359150602083013561455481614982565b809150509250929050565b60008060008060808587031215614574578182fd5b84359350602085013561458681614982565b925060408501359150606085013561428981614982565b6000806000606084860312156145b1578081fd5b833592506020840135915060408401356141e281614982565b6000806000806000608086880312156145e1578283fd5b8535945060208601359350604086013567ffffffffffffffff811115614605578384fd5b614611888289016140dd565b909450925050606086013561423d81614982565b6000815180845261463d816020860160208601614956565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff959095168552602085019390935260408401919091526060830152608082015260a00190565b6000602080830181845280855180835260408601915060408482028701019250838701855b8281101561474c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261473a858351614625565b94509285019290850190600101614700565b5092979650505050505050565b90815260200190565b60006020825261415e6020830184614625565b60208082526024908201527f676574537461626c65416d6f756e7473496e3a20696e636f7272656374206c6560408201527f6e67746800000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526023908201527f676574537461626c65496e666f3a20696e76616c696420706f6f6c206164647260408201527f6573730000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526012908201527f546f6f206d756368207265717565737465640000000000000000000000000000604082015260600190565b60208082526013908201527f546f6f206c6974746c6520726563656976656400000000000000000000000000604082015260600190565b600085825284602083015273ffffffffffffffffffffffffffffffffffffffff841660408301526080606083015261382f6080830184614625565b93845260208401929092526040830152606082015260800190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112614927578283fd5b83018035915067ffffffffffffffff821115614941578283fd5b6020019150368190038213156138d757600080fd5b60005b83811015614971578181015183820152602001614959565b838111156107bc5750506000910152565b73ffffffffffffffffffffffffffffffffffffffff81168114610a7657600080fdfea164736f6c6343000706000a0000000000000000000000001715a3e4a142d8b698131108995174f37aeba10d00000000000000000000000029ea7545def87022badc76323f373ea1e707c5230000000000000000000000001973e61a0518a3cfcbfb7a840ee9ac7ba35562a0000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a27
Deployed ByteCode
0x6080604052600436106101bb5760003560e01c80639b5c7a81116100ec578063cafe95a01161008a578063e0e189a011610064578063e0e189a014610460578063e90a182f14610473578063f2d5d56b14610486578063f3995c671461049957610266565b8063cafe95a014610427578063d4ef38de1461043a578063df2ab5bb1461044d57610266565b8063ac9650d8116100c6578063ac9650d8146103d9578063b85aa7af146103ec578063c2e3140a14610401578063c8bb18561461041457610266565b80639b5c7a81146103a0578063a4a78f0c146103b3578063ab0acea4146103c657610266565b806349404b7c116101595780634aa94288116101335780634aa94288146103525780635ae401dc1461036557806368e0d4e1146103785780639b2c0a371461038d57610266565b806349404b7c14610317578063496169971461032a5780634aa4a4fc1461033d57610266565b80631f0464d1116101955780631f0464d1146102af5780633068c554146102cf5780633957f453146102e25780634659a4941461030457610266565b806312210e8a1461026b5780631c58db4f146102735780631d4389f51461028657610266565b36610266573373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a27161461026457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f4e6f742057455448390000000000000000000000000000000000000000000000604482015290519081900360640190fd5b005b600080fd5b6102646104ac565b610264610281366004614500565b6104be565b6102996102943660046145ca565b610542565b6040516102a69190614759565b60405180910390f35b6102c26102bd3660046143c8565b610725565b6040516102a691906146db565b6102646102dd36600461424b565b6107af565b3480156102ee57600080fd5b506102f76107c2565b6040516102a6919061467f565b610264610312366004614294565b6107e6565b610264610325366004614530565b6108a6565b610264610338366004614500565b610a6c565b34801561034957600080fd5b506102f7610a79565b6102996103603660046142f4565b610a9d565b6102c26103733660046143c8565b610de5565b34801561038457600080fd5b506102f7610e5e565b61026461039b36600461455f565b610e82565b6102996103ae3660046145ca565b611098565b6102646103c1366004614294565b61144c565b6102996103d43660046145ca565b611521565b6102c26103e7366004614388565b6117cd565b3480156103f857600080fd5b506102f7611927565b61026461040f366004614294565b611943565b6102996104223660046142f4565b6119f8565b6102996104353660046145ca565b611c70565b61026461044836600461459d565b611e21565b61026461045b3660046141ac565b611e2d565b61026461046e3660046141ed565b611f44565b610264610481366004614181565b6120aa565b610264610494366004614181565b6120b9565b6102646104a7366004614294565b6120c5565b47156104bc576104bc334761215d565b565b7f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2773ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561052657600080fd5b505af115801561053a573d6000803e3d6000fd5b505050505050565b60006105a27f0000000000000000000000001715a3e4a142d8b698131108995174f37aeba10d878686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506122ab92505050565b6000815181106105ae57fe5b60200260200101519050848111156105fb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f29061482f565b60405180910390fd5b6106948484600081811061060b57fe5b90506020020160208101906106209190614142565b3361068e7f0000000000000000000000001715a3e4a142d8b698131108995174f37aeba10d8888600081811061065257fe5b90506020020160208101906106679190614142565b8989600181811061067457fe5b90506020020160208101906106899190614142565b6123e4565b846124cf565b73ffffffffffffffffffffffffffffffffffffffff8216600114156106bb573391506106de565b73ffffffffffffffffffffffffffffffffffffffff8216600214156106de573091505b61071c8484808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508692506126ad915050565b95945050505050565b6060838060014303401461079a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f426c6f636b686173680000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6107a484846117cd565b91505b509392505050565b6107bc8484338585611f44565b50505050565b7f0000000000000000000000001715a3e4a142d8b698131108995174f37aeba10d81565b604080517f8fcbaf0c00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101879052606481018690526001608482015260ff851660a482015260c4810184905260e48101839052905173ffffffffffffffffffffffffffffffffffffffff881691638fcbaf0c9161010480830192600092919082900301818387803b15801561088657600080fd5b505af115801561089a573d6000803e3d6000fd5b50505050505050505050565b60007f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2773ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561092f57600080fd5b505afa158015610943573d6000803e3d6000fd5b505050506040513d602081101561095957600080fd5b50519050828110156109cc57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e73756666696369656e742057455448390000000000000000000000000000604482015290519081900360640190fd5b8015610a67577f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2773ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015610a4557600080fd5b505af1158015610a59573d6000803e3d6000fd5b50505050610a67828261215d565b505050565b610a7681336108a6565b50565b7f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2781565b600060026000541415610b1157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600090815588888281610b2257fe5b9050602002016020810190610b379190614142565b9050600089897fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101818110610b6957fe5b9050602002016020810190610b7e9190614142565b9050600086610c3057506040517f70a0823100000000000000000000000000000000000000000000000000000000815260019073ffffffffffffffffffffffffffffffffffffffff8416906370a0823190610bdd90309060040161467f565b60206040518083038186803b158015610bf557600080fd5b505afa158015610c09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2d9190614518565b96505b80610c4157610c418333308a6124cf565b610cae8b8b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808f0282810182019093528e82529093508e92508d9182918501908490808284376000920191909152506129b292505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316906370a0823190610d0090309060040161467f565b60206040518083038186803b158015610d1857600080fd5b505afa158015610d2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d509190614518565b935085841015610d5f57600080fd5b73ffffffffffffffffffffffffffffffffffffffff851660011415610d8657339450610da9565b73ffffffffffffffffffffffffffffffffffffffff851660021415610da9573094505b73ffffffffffffffffffffffffffffffffffffffff85163014610dd257610dd2823087876124cf565b5050600160005550979650505050505050565b60608380610df1612b59565b111561079a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f5472616e73616374696f6e20746f6f206f6c6400000000000000000000000000604482015290519081900360640190fd5b7f00000000000000000000000029ea7545def87022badc76323f373ea1e707c52381565b600082118015610e93575060648211155b610e9c57600080fd5b60007f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2773ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015610f2557600080fd5b505afa158015610f39573d6000803e3d6000fd5b505050506040513d6020811015610f4f57600080fd5b5051905084811015610fc257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e73756666696369656e742057455448390000000000000000000000000000604482015290519081900360640190fd5b8015611091577f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2773ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561103b57600080fd5b505af115801561104f573d6000803e3d6000fd5b50505050600061271061106b8584612b5d90919063ffffffff16565b8161107257fe5b049050801561108557611085838261215d565b61053a8582840361215d565b5050505050565b6000808661114e5750600184846000816110ae57fe5b90506020020160208101906110c39190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016110fb919061467f565b60206040518083038186803b15801561111357600080fd5b505afa158015611127573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114b9190614518565b96505b6111d98585600081811061115e57fe5b90506020020160208101906111739190614142565b8261117e5733611180565b305b6111d37f0000000000000000000000001715a3e4a142d8b698131108995174f37aeba10d898960008181106111b157fe5b90506020020160208101906111c69190614142565b8a8a600181811061067457fe5b8a6124cf565b73ffffffffffffffffffffffffffffffffffffffff83166001141561120057339250611223565b73ffffffffffffffffffffffffffffffffffffffff831660021415611223573092505b600085857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810181811061125357fe5b90506020020160208101906112689190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231856040518263ffffffff1660e01b81526004016112a0919061467f565b60206040518083038186803b1580156112b857600080fd5b505afa1580156112cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112f09190614518565b90506113308686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508892506126ad915050565b6114058187877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810181811061136257fe5b90506020020160208101906113779190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231876040518263ffffffff1660e01b81526004016113af919061467f565b60206040518083038186803b1580156113c757600080fd5b505afa1580156113db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ff9190614518565b90612b87565b925086831015611441576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f290614866565b505095945050505050565b604080517fdd62ed3e00000000000000000000000000000000000000000000000000000000815233600482015230602482015290517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9173ffffffffffffffffffffffffffffffffffffffff89169163dd62ed3e91604480820192602092909190829003018186803b1580156114e157600080fd5b505afa1580156114f5573d6000803e3d6000fd5b505050506040513d602081101561150b57600080fd5b5051101561053a5761053a8686868686866107e6565b600080866115d757506001848460008161153757fe5b905060200201602081019061154c9190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401611584919061467f565b60206040518083038186803b15801561159c57600080fd5b505afa1580156115b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115d49190614518565b96505b611676858560008181106115e757fe5b90506020020160208101906115fc9190614142565b826116075733611609565b305b6111d37f00000000000000000000000029ea7545def87022badc76323f373ea1e707c5238989600081811061163a57fe5b905060200201602081019061164f9190614142565b8a8a600181811061165c57fe5b90506020020160208101906116719190614142565b612b97565b73ffffffffffffffffffffffffffffffffffffffff83166001141561169d573392506116c0565b73ffffffffffffffffffffffffffffffffffffffff8316600214156116c0573092505b600085857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018181106116f057fe5b90506020020160208101906117059190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231856040518263ffffffff1660e01b815260040161173d919061467f565b60206040518083038186803b15801561175557600080fd5b505afa158015611769573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178d9190614518565b9050611330868680806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250889250612c82915050565b60608167ffffffffffffffff811180156117e657600080fd5b5060405190808252806020026020018201604052801561181a57816020015b60608152602001906001900390816118055790505b50905060005b82811015611920576000803086868581811061183857fe5b905060200281019061184a91906148f3565b60405161185892919061466f565b600060405180830381855af49150503d8060008114611893576040519150601f19603f3d011682016040523d82523d6000602084013e611898565b606091505b5091509150816118fe576044815110156118b157600080fd5b600481019050808060200190518101906118cb9190614412565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f29190614762565b8084848151811061190b57fe5b60209081029190910101525050600101611820565b5092915050565b60015473ffffffffffffffffffffffffffffffffffffffff1681565b604080517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523360048201523060248201529051869173ffffffffffffffffffffffffffffffffffffffff89169163dd62ed3e91604480820192602092909190829003018186803b1580156119b857600080fd5b505afa1580156119cc573d6000803e3d6000fd5b505050506040513d60208110156119e257600080fd5b5051101561053a5761053a8686868686866120c5565b600060026000541415611a6c57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600081905550611aee86866000818110611a8457fe5b9050602002016020810190611a999190614142565b600154604080516020808d0282810182019093528c825273ffffffffffffffffffffffffffffffffffffffff9093169290918d918d9182918501908490808284376000920191909152508a9250612f87915050565b600081518110611afa57fe5b6020026020010151905082811115611b1157600080fd5b611b3e88886000818110611b2157fe5b9050602002016020810190611b369190614142565b3330846124cf565b611bab88888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808c0282810182019093528b82529093508b92508a9182918501908490808284376000920191909152506129b292505050565b73ffffffffffffffffffffffffffffffffffffffff821660011415611bd257339150611bf5565b73ffffffffffffffffffffffffffffffffffffffff821660021415611bf5573091505b73ffffffffffffffffffffffffffffffffffffffff82163014611c6057611c6088887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101818110611c4357fe5b9050602002016020810190611c589190614142565b3084876124cf565b6001600055979650505050505050565b6000611cd07f00000000000000000000000029ea7545def87022badc76323f373ea1e707c5238786868080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061319f92505050565b600081518110611cdc57fe5b6020026020010151905084811115611d20576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f29061482f565b611d9984846000818110611d3057fe5b9050602002016020810190611d459190614142565b3361068e7f00000000000000000000000029ea7545def87022badc76323f373ea1e707c52388886000818110611d7757fe5b9050602002016020810190611d8c9190614142565b8989600181811061165c57fe5b73ffffffffffffffffffffffffffffffffffffffff821660011415611dc057339150611de3565b73ffffffffffffffffffffffffffffffffffffffff821660021415611de3573091505b61071c848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250869250612c82915050565b610a6783338484610e82565b60008373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611e9657600080fd5b505afa158015611eaa573d6000803e3d6000fd5b505050506040513d6020811015611ec057600080fd5b5051905082811015611f3357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e73756666696369656e7420746f6b656e0000000000000000000000000000604482015290519081900360640190fd5b80156107bc576107bc8483836132c9565b600082118015611f55575060648211155b611f5e57600080fd5b60008573ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611fc757600080fd5b505afa158015611fdb573d6000803e3d6000fd5b505050506040513d6020811015611ff157600080fd5b505190508481101561206457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e73756666696369656e7420746f6b656e0000000000000000000000000000604482015290519081900360640190fd5b801561053a5760006127106120798386612b5d565b8161208057fe5b0490508015612094576120948784836132c9565b6120a187868385036132c9565b50505050505050565b6120b5828233611e2d565b5050565b6120b58233308461349e565b604080517fd505accf000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018790526064810186905260ff8516608482015260a4810184905260c48101839052905173ffffffffffffffffffffffffffffffffffffffff88169163d505accf9160e480830192600092919082900301818387803b15801561088657600080fd5b6040805160008082526020820190925273ffffffffffffffffffffffffffffffffffffffff84169083906040518082805190602001908083835b602083106121d457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101612197565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114612236576040519150601f19603f3d011682016040523d82523d6000602084013e61223b565b606091505b5050905080610a6757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f5354450000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60606002825110156122bc57600080fd5b815167ffffffffffffffff811180156122d457600080fd5b506040519080825280602002602001820160405280156122fe578160200160208202803683370190505b509050828160018351038151811061231257fe5b602090810291909101015281517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff015b80156107a75760008061237f8786600186038151811061235e57fe5b602002602001015187868151811061237257fe5b602002602001015161367b565b915091506123a184848151811061239257fe5b60200260200101518383613763565b8460018503815181106123b057fe5b602090810291909101015250507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01612342565b60008060006123f38585613839565b604080517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501207fff0000000000000000000000000000000000000000000000000000000000000060688401529a90941b9093166069840152607d8301989098527f59fffffddd756cba9095128e53f3291a6ba38b21e3df744936e7289326555d62609d808401919091528851808403909101815260bd909201909752805196019590952095945050505050565b7f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2773ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614801561252a5750804710155b15612673577f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2773ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561259757600080fd5b505af11580156125ab573d6000803e3d6000fd5b50505050507f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2773ffffffffffffffffffffffffffffffffffffffff1663a9059cbb83836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15801561264157600080fd5b505af1158015612655573d6000803e3d6000fd5b505050506040513d602081101561266b57600080fd5b506107bc9050565b73ffffffffffffffffffffffffffffffffffffffff83163014156126a15761269c8483836132c9565b6107bc565b6107bc8484848461349e565b60005b6001835103811015610a67576000808483815181106126cb57fe5b60200260200101518584600101815181106126e257fe5b60200260200101519150915060006126fa8383613839565b509050600061272a7f0000000000000000000000001715a3e4a142d8b698131108995174f37aeba10d85856123e4565b90506000806000808473ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561277857600080fd5b505afa15801561278c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127b091906144bd565b506dffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff1691506000808773ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff1614612812578284612815565b83835b91509150612856828b73ffffffffffffffffffffffffffffffffffffffff166370a082318a6040518263ffffffff1660e01b81526004016113af919061467f565b95506128638683836138de565b9450505050506000808573ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16146128a7578260006128ab565b6000835b91509150600060028c51038a106128c2578a612903565b6129037f0000000000000000000000001715a3e4a142d8b698131108995174f37aeba10d898e8d600201815181106128f657fe5b60200260200101516123e4565b604080516000815260208101918290527f022c0d9f0000000000000000000000000000000000000000000000000000000090915290915073ffffffffffffffffffffffffffffffffffffffff87169063022c0d9f9061296b908690869086906024810161489d565b600060405180830381600087803b15801561298557600080fd5b505af1158015612999573d6000803e3d6000fd5b50506001909b019a506126b09950505050505050505050565b60005b6001835103811015610a67576000808483815181106129d057fe5b60200260200101518584600101815181106129e757fe5b602002602001015191509150600080612a14868681518110612a0557fe5b602002602001015185856139b4565b9150915060008473ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401612a53919061467f565b60206040518083038186803b158015612a6b57600080fd5b505afa158015612a7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aa39190614518565b9050612ac385888881518110612ab557fe5b602002602001015183613ed8565b868681518110612acf57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16635b41b90884848460006040518563ffffffff1660e01b8152600401612b1694939291906148d8565b600060405180830381600087803b158015612b3057600080fd5b505af1158015612b44573d6000803e3d6000fd5b5050600190970196506129b595505050505050565b4290565b6000821580612b7857505081810281838281612b7557fe5b04145b612b8157600080fd5b92915050565b80820382811115612b8157600080fd5b6000806000612ba68585613839565b604080517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501207fff0000000000000000000000000000000000000000000000000000000000000060688401529a90941b9093166069840152607d8301989098527f5dff1ac2d132f5ac2841294c6e9fc0ebafae8d447fac7996ef21c21112f411f1609d808401919091528851808403909101815260bd909201909752805196019590952095945050505050565b60005b6001835103811015610a6757600080848381518110612ca057fe5b6020026020010151858460010181518110612cb757fe5b6020026020010151915091506000612ccf8383613839565b5090506000612cff7f00000000000000000000000029ea7545def87022badc76323f373ea1e707c5238585612b97565b90506000806000808473ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b158015612d4d57600080fd5b505afa158015612d61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d8591906144bd565b506dffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff1691506000808773ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff1614612de7578284612dea565b83835b91509150612e2b828b73ffffffffffffffffffffffffffffffffffffffff166370a082318a6040518263ffffffff1660e01b81526004016113af919061467f565b9550612e388683836138de565b9450505050506000808573ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614612e7c57826000612e80565b6000835b91509150600060028c51038a10612e97578a612ed8565b612ed87f00000000000000000000000029ea7545def87022badc76323f373ea1e707c523898e8d60020181518110612ecb57fe5b6020026020010151612b97565b604080516000815260208101918290527f022c0d9f0000000000000000000000000000000000000000000000000000000090915290915073ffffffffffffffffffffffffffffffffffffffff87169063022c0d9f90612f40908690869086906024810161489d565b600060405180830381600087803b158015612f5a57600080fd5b505af1158015612f6e573d6000803e3d6000fd5b50506001909b019a50612c859950505050505050505050565b81516060906002811015612fc7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f290614775565b8067ffffffffffffffff81118015612fde57600080fd5b50604051908082528060200260200182016040528015613008578160200160208202803683370190505b5091508282600183038151811061301b57fe5b60209081029190910101527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81015b801561319557600060018203905060008061308c8a89858151811061306b57fe5b60200260200101518a878151811061307f57fe5b60200260200101516139b4565b915091508873ffffffffffffffffffffffffffffffffffffffff1663ca4bc7148b84848a89815181106130bb57fe5b60200260200101517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518663ffffffff1660e01b81526004016131049594939291906146a0565b60206040518083038186803b15801561311c57600080fd5b505afa158015613130573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131549190614518565b86848151811061316057fe5b60209081029190910101525050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0161304a565b5050949350505050565b60606002825110156131b057600080fd5b815167ffffffffffffffff811180156131c857600080fd5b506040519080825280602002602001820160405280156131f2578160200160208202803683370190505b509050828160018351038151811061320657fe5b602090810291909101015281517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff015b80156107a7576000806132738786600186038151811061325257fe5b602002602001015187868151811061326657fe5b60200260200101516140ad565b9150915061328684848151811061239257fe5b84600185038151811061329557fe5b602090810291909101015250507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01613236565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001781529251825160009485949389169392918291908083835b6020831061339e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613361565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613400576040519150601f19603f3d011682016040523d82523d6000602084013e613405565b606091505b5091509150818015613433575080511580613433575080806020019051602081101561343057600080fd5b50515b61109157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f5354000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6040805173ffffffffffffffffffffffffffffffffffffffff85811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd00000000000000000000000000000000000000000000000000000000178152925182516000948594938a169392918291908083835b6020831061357b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161353e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146135dd576040519150601f19603f3d011682016040523d82523d6000602084013e6135e2565b606091505b5091509150818015613610575080511580613610575080806020019051602081101561360d57600080fd5b50515b61053a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f5354460000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600080600061368a8585613839565b50905060008061369b8888886123e4565b73ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b1580156136e057600080fd5b505afa1580156136f4573d6000803e3d6000fd5b505050506040513d606081101561370a57600080fd5b5080516020909101516dffffffffffffffffffffffffffff918216935016905073ffffffffffffffffffffffffffffffffffffffff87811690841614613751578082613754565b81815b90999098509650505050505050565b60008084116137d357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f494e53554646494349454e545f4f55545055545f414d4f554e54000000000000604482015290519081900360640190fd5b6000831180156137e35750600082115b6137ec57600080fd5b60006138046127106137fe8688612b5d565b90612b5d565b905060006138186126f36137fe8689612b87565b905061382f600182848161382857fe5b04906140cd565b9695505050505050565b6000808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16141561387557600080fd5b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16106138af5782846138b2565b83835b909250905073ffffffffffffffffffffffffffffffffffffffff82166138d757600080fd5b9250929050565b600080841161394e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f494e53554646494349454e545f494e5055545f414d4f554e5400000000000000604482015290519081900360640190fd5b60008311801561395e5750600082115b61396757600080fd5b6000613975856126f3612b5d565b905060006139838285612b5d565b9050600061399d8361399788612710612b5d565b906140cd565b90508082816139a857fe5b04979650505050505050565b60008073ffffffffffffffffffffffffffffffffffffffff8516613a04576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f2906147d2565b600085905060008173ffffffffffffffffffffffffffffffffffffffff1663293577506040518163ffffffff1660e01b815260040160206040518083038186803b158015613a5157600080fd5b505afa158015613a65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a899190614518565b90508060021415613b98576040517fc661065700000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff84169063c661065790613ae9908490600401614759565b60206040518083038186803b158015613b0157600080fd5b505afa158015613b15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b399190614165565b90508073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614613b75576001613b78565b60005b60ff1694508415613b8a576000613b8d565b60015b60ff16935050613ece565b8060031415613ece576040517fc661065700000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff84169063c661065790613bf6908490600401614759565b60206040518083038186803b158015613c0e57600080fd5b505afa158015613c22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c469190614165565b905060008373ffffffffffffffffffffffffffffffffffffffff1663c661065760016040518263ffffffff1660e01b8152600401613c849190614759565b60206040518083038186803b158015613c9c57600080fd5b505afa158015613cb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cd49190614165565b905060008473ffffffffffffffffffffffffffffffffffffffff1663c661065760026040518263ffffffff1660e01b8152600401613d129190614759565b60206040518083038186803b158015613d2a57600080fd5b505afa158015613d3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d629190614165565b90508273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415613da15760009650613e17565b8173ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415613dde5760019650613e17565b8073ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415613e1757600296505b8273ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415613e545760009550613eca565b8173ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415613e915760019550613eca565b8073ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415613eca57600295505b5050505b5050935093915050565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b3000000000000000000000000000000000000000000000000000000001781529251825160009485949389169392918291908083835b60208310613fad57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613f70565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461400f576040519150601f19603f3d011682016040523d82523d6000602084013e614014565b606091505b5091509150818015614042575080511580614042575080806020019051602081101561403f57600080fd5b50515b61109157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f5341000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60008060006140bc8585613839565b50905060008061369b888888612b97565b80820182811015612b8157600080fd5b60008083601f8401126140ee578081fd5b50813567ffffffffffffffff811115614105578182fd5b60208301915083602080830285010111156138d757600080fd5b80516dffffffffffffffffffffffffffff8116811461413d57600080fd5b919050565b600060208284031215614153578081fd5b813561415e81614982565b9392505050565b600060208284031215614176578081fd5b815161415e81614982565b60008060408385031215614193578081fd5b823561419e81614982565b946020939093013593505050565b6000806000606084860312156141c0578081fd5b83356141cb81614982565b92506020840135915060408401356141e281614982565b809150509250925092565b600080600080600060a08688031215614204578081fd5b853561420f81614982565b945060208601359350604086013561422681614982565b925060608601359150608086013561423d81614982565b809150509295509295909350565b60008060008060808587031215614260578384fd5b843561426b81614982565b93506020850135925060408501359150606085013561428981614982565b939692955090935050565b60008060008060008060c087890312156142ac578081fd5b86356142b781614982565b95506020870135945060408701359350606087013560ff811681146142da578182fd5b9598949750929560808101359460a0909101359350915050565b600080600080600080600060a0888a03121561430e578081fd5b873567ffffffffffffffff80821115614325578283fd5b6143318b838c016140dd565b909950975060208a0135915080821115614349578283fd5b506143568a828b016140dd565b9096509450506040880135925060608801359150608088013561437881614982565b8091505092959891949750929550565b6000806020838503121561439a578081fd5b823567ffffffffffffffff8111156143b0578182fd5b6143bc858286016140dd565b90969095509350505050565b6000806000604084860312156143dc578081fd5b83359250602084013567ffffffffffffffff8111156143f9578182fd5b614405868287016140dd565b9497909650939450505050565b600060208284031215614423578081fd5b815167ffffffffffffffff8082111561443a578283fd5b818401915084601f83011261444d578283fd5b81518181111561445957fe5b60405160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116820101818110848211171561449557fe5b6040528181528382016020018710156144ac578485fd5b61382f826020830160208701614956565b6000806000606084860312156144d1578081fd5b6144da8461411f565b92506144e86020850161411f565b9150604084015163ffffffff811681146141e2578182fd5b600060208284031215614511578081fd5b5035919050565b600060208284031215614529578081fd5b5051919050565b60008060408385031215614542578182fd5b82359150602083013561455481614982565b809150509250929050565b60008060008060808587031215614574578182fd5b84359350602085013561458681614982565b925060408501359150606085013561428981614982565b6000806000606084860312156145b1578081fd5b833592506020840135915060408401356141e281614982565b6000806000806000608086880312156145e1578283fd5b8535945060208601359350604086013567ffffffffffffffff811115614605578384fd5b614611888289016140dd565b909450925050606086013561423d81614982565b6000815180845261463d816020860160208601614956565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff959095168552602085019390935260408401919091526060830152608082015260a00190565b6000602080830181845280855180835260408601915060408482028701019250838701855b8281101561474c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261473a858351614625565b94509285019290850190600101614700565b5092979650505050505050565b90815260200190565b60006020825261415e6020830184614625565b60208082526024908201527f676574537461626c65416d6f756e7473496e3a20696e636f7272656374206c6560408201527f6e67746800000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526023908201527f676574537461626c65496e666f3a20696e76616c696420706f6f6c206164647260408201527f6573730000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526012908201527f546f6f206d756368207265717565737465640000000000000000000000000000604082015260600190565b60208082526013908201527f546f6f206c6974746c6520726563656976656400000000000000000000000000604082015260600190565b600085825284602083015273ffffffffffffffffffffffffffffffffffffffff841660408301526080606083015261382f6080830184614625565b93845260208401929092526040830152606082015260800190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112614927578283fd5b83018035915067ffffffffffffffff821115614941578283fd5b6020019150368190038213156138d757600080fd5b60005b83811015614971578181015183820152602001614959565b838111156107bc5750506000910152565b73ffffffffffffffffffffffffffffffffffffffff81168114610a7657600080fdfea164736f6c6343000706000a