Contract Address Details
contract

0x0B0704f4F7813fA86d0DCc74E78E8447adB5Dbd7

Contract Name
GnosisProtocolRelayer
Creator
0x5ef09c–178794 at 0x53bdcf–d3da9b
Balance
301.979663272 xDAI ( )
Tokens
Fetching tokens...
Transactions
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
19218018
Contract name:
GnosisProtocolRelayer




Optimization enabled
true
Compiler version
v0.6.6+commit.6c089d02




Optimization runs
200
EVM Version
default




Verified at
2020-12-19 15:20:10.416256Z

Constructor Arguments

000000000000000000000000e716ec63c5673b3a4732d22909b38d779fa47c3f00000000000000000000000025b06305cc4ec6afcf3e7c0b673da1ef8ae2631300000000000000000000000025b06305cc4ec6afcf3e7c0b673da1ef8ae2631300000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000e91d153e0b41518a2ce8dd3d7944fa863463a97d00000000000000000000000043b7b9a571e6b578f8e0e2e7ba05f960c40cb0f00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a818b4f111ccac7aa31d0bcc0806d64f2e0737d7
              

Contract source code

// File: contracts/libraries/TransferHelper.sol
pragma solidity >=0.6.0;
// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
function safeApprove(
address token,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('approve(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: APPROVE_FAILED");
}
function safeTransfer(
address token,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: TRANSFER_FAILED");
}
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: TRANSFER_FROM_FAILED");
}
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, "TransferHelper: ETH_TRANSFER_FAILED");
}
}
// File: contracts/libraries/SafeMath.sol
pragma solidity =0.6.6;
// a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
library SafeMath {
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x, "ds-math-add-overflow");
}
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x, "ds-math-sub-underflow");
}
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
}
}
// File: dxswap-core/contracts/interfaces/IDXswapPair.sol
pragma solidity >=0.5.0;
interface IDXswapPair {
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);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
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 swapFee() external view returns (uint32);
function mint(address to) external returns (uint liquidity);
function burn(address to) 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;
function setSwapFee(uint32) external;
}
// File: contracts/libraries/DXswapLibrary.sol
pragma solidity >=0.5.0;
library DXswapLibrary {
using SafeMath for uint256;
// returns sorted token addresses, used to handle return values from pairs sorted in this order
function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
require(tokenA != tokenB, "DXswapLibrary: IDENTICAL_ADDRESSES");
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0), "DXswapLibrary: ZERO_ADDRESS");
}
// calculates the CREATE2 address for a pair without making any external calls
function pairFor(
address factory,
address tokenA,
address tokenB
) internal pure returns (address pair) {
(address token0, address token1) = sortTokens(tokenA, tokenB);
pair = address(
uint256(
keccak256(
abi.encodePacked(
hex"ff",
factory,
keccak256(abi.encodePacked(token0, token1)),
hex"2db943b381c6ef706828ea5e89f480bd449d4d3a2b98e6da97b30d0eb41fb6d6" // 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, ) = IDXswapPair(pairFor(factory, tokenA, tokenB)).getReserves();
(reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
// fetches and sorts the reserves for a pair
function getSwapFee(
address factory,
address tokenA,
address tokenB
) internal view returns (uint256 swapFee) {
(address token0, ) = sortTokens(tokenA, tokenB);
swapFee = IDXswapPair(pairFor(factory, tokenA, tokenB)).swapFee();
}
// given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
function quote(
uint256 amountA,
uint256 reserveA,
uint256 reserveB
) internal pure returns (uint256 amountB) {
require(amountA > 0, "DXswapLibrary: INSUFFICIENT_AMOUNT");
require(reserveA > 0 && reserveB > 0, "DXswapLibrary: INSUFFICIENT_LIQUIDITY");
amountB = amountA.mul(reserveB) / reserveA;
}
// given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
function getAmountOut(
uint256 amountIn,
uint256 reserveIn,
uint256 reserveOut,
uint256 swapFee
) internal pure returns (uint256 amountOut) {
require(amountIn > 0, "DXswapLibrary: INSUFFICIENT_INPUT_AMOUNT");
require(reserveIn > 0 && reserveOut > 0, "DXswapLibrary: INSUFFICIENT_LIQUIDITY");
uint256 amountInWithFee = amountIn.mul(uint256(10000).sub(swapFee));
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,
uint256 swapFee
) internal pure returns (uint256 amountIn) {
require(amountOut > 0, "DXswapLibrary: INSUFFICIENT_OUTPUT_AMOUNT");
require(reserveIn > 0 && reserveOut > 0, "DXswapLibrary: INSUFFICIENT_LIQUIDITY");
uint256 numerator = reserveIn.mul(amountOut).mul(10000);
uint256 denominator = reserveOut.sub(amountOut).mul(uint256(10000).sub(swapFee));
amountIn = (numerator / denominator).add(1);
}
// performs chained getAmountOut calculations on any number of pairs
function getAmountsOut(
address factory,
uint256 amountIn,
address[] memory path
) internal view returns (uint256[] memory amounts) {
require(path.length >= 2, "DXswapLibrary: INVALID_PATH");
amounts = new uint256[](path.length);
amounts[0] = amountIn;
for (uint256 i; i < path.length - 1; i++) {
(uint256 reserveIn, uint256 reserveOut) = getReserves(factory, path[i], path[i + 1]);
amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut, getSwapFee(factory, path[i], path[i + 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, "DXswapLibrary: INVALID_PATH");
amounts = new uint256[](path.length);
amounts[amounts.length - 1] = amountOut;
for (uint256 i = path.length - 1; i > 0; i--) {
(uint256 reserveIn, uint256 reserveOut) = getReserves(factory, path[i - 1], path[i]);
amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut, getSwapFee(factory, path[i - 1], path[i]));
}
}
}
// File: contracts/interfaces/IDXswapFactory.sol
pragma solidity >=0.5.0;
interface IDXswapFactory {
event PairCreated(address indexed token0, address indexed token1, address pair, uint256);
function INIT_CODE_PAIR_HASH() external pure returns (bytes32);
function feeTo() external view returns (address);
function protocolFeeDenominator() external view returns (uint8);
function feeToSetter() external view returns (address);
function getPair(address tokenA, address tokenB) external view returns (address pair);
function allPairs(uint256) external view returns (address pair);
function allPairsLength() external view returns (uint256);
function createPair(address tokenA, address tokenB) external returns (address pair);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
function setProtocolFee(uint8 _protocolFee) external;
function setSwapFee(address pair, uint32 swapFee) external;
}
// File: contracts/interfaces/IERC20.sol
pragma solidity >=0.5.0;
interface IERC20 {
event Approval(address indexed owner, address indexed spender, uint256 value);
event Transfer(address indexed from, address indexed to, uint256 value);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address owner) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(
address from,
address to,
uint256 value
) external returns (bool);
}
// File: contracts/interfaces/IWETH.sol
pragma solidity >=0.5.0;
interface IWETH {
function deposit() external payable;
function transfer(address to, uint256 value) external returns (bool);
function withdraw(uint256) external;
function balanceOf(address guy) external returns (uint256);
function approve(address guy, uint256 wad) external returns (bool);
}
// File: contracts/interfaces/IBatchExchange.sol
pragma solidity >=0.5.0;
interface IBatchExchange {
function tokenAddressToIdMap(address addr) external view returns (uint16);
function tokenIdToAddressMap(uint16 id) external view returns (address);
function hasToken(address addr) external view returns (bool);
function placeOrder(
uint16 buyToken,
uint16 sellToken,
uint32 validUntil,
uint128 buyAmount,
uint128 sellAmount
) external returns (uint256);
function cancelOrders(uint16[] calldata orderIds) external;
}
// File: contracts/interfaces/IEpochTokenLocker.sol
pragma solidity >=0.5.0;
interface IEpochTokenLocker {
function deposit(address token, uint256 amount) external;
function withdraw(address user, address token) external;
function getCurrentBatchId() external view returns (uint32);
function BATCH_TIME() external view returns (uint32);
}
// File: contracts/libraries/FixedPoint.sol
pragma solidity >=0.5.0;
// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
library FixedPoint {
// range: [0, 2**112 - 1]
// resolution: 1 / 2**112
struct uq112x112 {
uint224 _x;
}
// range: [0, 2**144 - 1]
// resolution: 1 / 2**112
struct uq144x112 {
uint256 _x;
}
uint8 private constant RESOLUTION = 112;
// encode a uint112 as a UQ112x112
function encode(uint112 x) internal pure returns (uq112x112 memory) {
return uq112x112(uint224(x) << RESOLUTION);
}
// encodes a uint144 as a UQ144x112
function encode144(uint144 x) internal pure returns (uq144x112 memory) {
return uq144x112(uint256(x) << RESOLUTION);
}
// divide a UQ112x112 by a uint112, returning a UQ112x112
function div(uq112x112 memory self, uint112 x) internal pure returns (uq112x112 memory) {
require(x != 0, "FixedPoint: DIV_BY_ZERO");
return uq112x112(self._x / uint224(x));
}
// multiply a UQ112x112 by a uint, returning a UQ144x112
// reverts on overflow
function mul(uq112x112 memory self, uint256 y) internal pure returns (uq144x112 memory) {
uint256 z;
require(y == 0 || (z = uint256(self._x) * y) / y == uint256(self._x), "FixedPoint: MULTIPLICATION_OVERFLOW");
return uq144x112(z);
}
// returns a UQ112x112 which represents the ratio of the numerator to the denominator
// equivalent to encode(numerator).div(denominator)
function fraction(uint112 numerator, uint112 denominator) internal pure returns (uq112x112 memory) {
require(denominator > 0, "FixedPoint: DIV_BY_ZERO");
return uq112x112((uint224(numerator) << RESOLUTION) / denominator);
}
// decode a UQ112x112 into a uint112 by truncating after the radix point
function decode(uq112x112 memory self) internal pure returns (uint112) {
return uint112(self._x >> RESOLUTION);
}
// decode a UQ144x112 into a uint144 by truncating after the radix point
function decode144(uq144x112 memory self) internal pure returns (uint144) {
return uint144(self._x >> RESOLUTION);
}
}
// File: contracts/libraries/DXswapOracleLibrary.sol
pragma solidity >=0.5.0;
// library with helper methods for oracles that are concerned with computing average prices
library DXswapOracleLibrary {
using FixedPoint for *;
// helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - 1]
function currentBlockTimestamp() internal view returns (uint32) {
return uint32(block.timestamp % 2**32);
}
// produces the cumulative price using counterfactuals to save gas and avoid a call to sync.
function currentCumulativePrices(address pair)
internal
view
returns (
uint256 price0Cumulative,
uint256 price1Cumulative,
uint32 blockTimestamp
)
{
blockTimestamp = currentBlockTimestamp();
price0Cumulative = IDXswapPair(pair).price0CumulativeLast();
price1Cumulative = IDXswapPair(pair).price1CumulativeLast();
// if time has elapsed since the last update on the pair, mock the accumulated price values
(uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IDXswapPair(pair).getReserves();
if (blockTimestampLast != blockTimestamp) {
// subtraction overflow is desired
uint32 timeElapsed = blockTimestamp - blockTimestampLast;
// addition overflow is desired
// counterfactual
price0Cumulative += uint256(FixedPoint.fraction(reserve1, reserve0)._x) * timeElapsed;
// counterfactual
price1Cumulative += uint256(FixedPoint.fraction(reserve0, reserve1)._x) * timeElapsed;
}
}
}
// File: contracts/OracleCreator.sol
pragma solidity =0.6.6;
contract OracleCreator {
using FixedPoint for *;
using SafeMath for uint256;
event OracleCreated(
uint256 indexed _oracleIndex,
address indexed _pair,
uint256 _windowTime
);
struct Oracle{
uint256 windowTime;
address token0;
address token1;
IDXswapPair pair;
uint32 blockTimestampLast;
uint256 price0CumulativeLast;
uint256 price1CumulativeLast;
FixedPoint.uq112x112 price0Average;
FixedPoint.uq112x112 price1Average;
uint256 observationsCount;
address owner;
}
mapping(uint256 => Oracle) public oracles;
uint256 public oraclesIndex;
function createOracle(
uint256 windowTime,
address pair
) public returns (uint256 oracleId) {
IDXswapPair sourcePair = IDXswapPair(pair);
address token0 = sourcePair.token0();
address token1 = sourcePair.token1();
(,, uint32 blockTimestampLast) = sourcePair.getReserves();
oracles[oraclesIndex] = Oracle({
windowTime: windowTime,
token0: token0,
token1: token1,
pair: sourcePair,
blockTimestampLast: blockTimestampLast,
price0CumulativeLast: sourcePair.price0CumulativeLast(),
price1CumulativeLast: sourcePair.price1CumulativeLast(),
price0Average: FixedPoint.uq112x112(0),
price1Average: FixedPoint.uq112x112(0),
observationsCount: 0,
owner: msg.sender
});
oracleId = oraclesIndex;
oraclesIndex++;
emit OracleCreated(oracleId, address(sourcePair), windowTime);
}
function update(uint256 oracleIndex) public {
Oracle storage oracle = oracles[oracleIndex];
require(msg.sender == oracle.owner, 'OracleCreator: CALLER_NOT_OWNER');
require(oracle.observationsCount < 2, 'OracleCreator: FINISHED_OBERSERVATION');
(uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) =
DXswapOracleLibrary.currentCumulativePrices(address(oracle.pair));
uint32 timeElapsed = blockTimestamp - oracle.blockTimestampLast; // overflow is desired
// first update can be executed immediately. Ensure that at least one full period has passed since the first update
require(
oracle.observationsCount == 0 || timeElapsed >= oracle.windowTime,
'OracleCreator: PERIOD_NOT_ELAPSED'
);
// overflow is desired, casting never truncates
// cumulative price is in (uq112x112 price * seconds) units so we simply wrap it after division by time elapsed
oracle.price0Average = FixedPoint.uq112x112(
uint224((price0Cumulative - oracle.price0CumulativeLast) / timeElapsed)
);
oracle.price1Average = FixedPoint.uq112x112(
uint224((price1Cumulative - oracle.price1CumulativeLast) / timeElapsed)
);
oracle.price0CumulativeLast = price0Cumulative;
oracle.price1CumulativeLast = price1Cumulative;
oracle.blockTimestampLast = blockTimestamp;
oracle.observationsCount++;
}
// note this will always return 0 before update has been called successfully for the first time.
function consult(uint256 oracleIndex, address token, uint256 amountIn) external view returns (uint256 amountOut) {
Oracle storage oracle = oracles[oracleIndex];
FixedPoint.uq112x112 memory avg;
if (token == oracle.token0) {
avg = oracle.price0Average;
} else {
require(token == oracle.token1, 'OracleCreator: INVALID_TOKEN');
avg = oracle.price1Average;
}
amountOut = avg.mul(amountIn).decode144();
}
function isOracleFinalized(uint256 oracleIndex) external view returns (bool){
return oracles[oracleIndex].observationsCount == 2;
}
function getOracleDetails(uint256 oracleIndex) external view returns (Oracle memory) {
return oracles[oracleIndex];
}
}
// File: contracts/GnosisProtocolRelayer.sol
pragma solidity =0.6.6;
pragma experimental ABIEncoderV2;
contract GnosisProtocolRelayer {
using SafeMath for uint256;
event NewOrder(
uint256 indexed _orderIndex
);
event PlacedTrade(
uint256 indexed _orderIndex,
uint256 _gpOrderID
);
event WithdrawnExpiredOrder(
uint256 indexed _orderIndex
);
struct Order {
address tokenIn;
address tokenOut;
uint128 tokenInAmount;
uint128 tokenOutAmount;
uint256 priceTolerance;
uint256 minReserve;
address oraclePair;
uint256 deadline;
uint256 oracleId;
uint256 gpOrderId;
address factory;
bool executed;
}
uint256 public immutable GAS_ORACLE_UPDATE = 168364;
uint256 public immutable PARTS_PER_MILLION = 1000000;
uint256 public immutable BOUNTY = 0.01 ether; // To be decided
uint256 public immutable ORACLE_WINDOW_TIME = 120; // 2 Minutes
uint32 public immutable BATCH_TIME;
uint32 public immutable UINT32_MAX_VALUE = 2**32 - 1;
uint128 public immutable UINT128_MAX_VALUE = 2**128 - 1;
address public immutable batchExchange;
address public immutable epochTokenLocker;
address payable public immutable owner;
address public immutable WETH;
OracleCreator public oracleCreator;
uint256 public orderCount;
mapping(uint256 => Order) public orders;
mapping(address => bool) public exchangeFactoryWhitelist;
constructor(
address payable _owner,
address _batchExchange,
address _epochTokenLocker,
address[] memory _factoryWhitelist,
address _WETH,
OracleCreator _oracleCreater
) public {
require(_factoryWhitelist.length > 0, 'GnosisProtocolRelayer: MISSING_FACTORY_WHITELIST');
batchExchange = _batchExchange;
epochTokenLocker = _epochTokenLocker;
oracleCreator = _oracleCreater;
owner = _owner;
WETH = _WETH;
BATCH_TIME = IEpochTokenLocker(_epochTokenLocker).BATCH_TIME();
for (uint i=0; i < _factoryWhitelist.length; i++) {
exchangeFactoryWhitelist[_factoryWhitelist[i]] = true;
}
}
function orderTrade(
address tokenIn,
address tokenOut,
uint128 tokenInAmount,
uint128 tokenOutAmount,
uint256 priceTolerance,
uint256 minReserve,
uint256 deadline,
address factory
) external payable returns (uint256 orderIndex) {
require(exchangeFactoryWhitelist[factory], 'GnosisProtocolRelayer: INVALID_FACTORY');
require(msg.sender == owner, 'GnosisProtocolRelayer: CALLER_NOT_OWNER');
require(tokenIn != tokenOut, 'GnosisProtocolRelayer: INVALID_PAIR');
require(tokenInAmount > 0 && tokenOutAmount > 0, 'GnosisProtocolRelayer: INVALID_TOKEN_AMOUNT');
require(priceTolerance <= PARTS_PER_MILLION, 'GnosisProtocolRelayer: INVALID_TOLERANCE');
require(deadline <= UINT32_MAX_VALUE, 'GnosisProtocolRelayer: INVALID_DEADLINE');
require(block.timestamp <= deadline, 'GnosisProtocolRelayer: DEADLINE_REACHED');
if (tokenIn == address(0)) {
require(msg.value >= tokenInAmount, 'GnosisProtocolRelayer: INSUFFIENT_ETH');
tokenIn = WETH;
IWETH(WETH).deposit{value: tokenInAmount}();
} else if (tokenOut == address(0)) {
tokenOut = WETH;
}
require(IERC20(tokenIn).balanceOf(address(this)) >= tokenInAmount, 'GnosisProtocolRelayer: INSUFFIENT_TOKEN_IN');
address pair = _pair(tokenIn, tokenOut, factory);
require(pair != address(0), 'GnosisProtocolRelayer: UNKOWN_PAIR');
orderIndex = _OrderIndex();
orders[orderIndex] = Order({
tokenIn: tokenIn,
tokenOut: tokenOut,
tokenInAmount: tokenInAmount,
tokenOutAmount: tokenOutAmount,
priceTolerance: priceTolerance,
minReserve: minReserve,
oraclePair: pair,
deadline: deadline,
oracleId: 0,
gpOrderId: 0,
factory: factory,
executed: false
});
/* Create an oracle to calculate average price */
orders[orderIndex].oracleId = oracleCreator.createOracle(ORACLE_WINDOW_TIME, pair);
emit NewOrder(orderIndex);
}
function placeTrade(uint256 orderIndex) external {
Order storage order = orders[orderIndex];
require(orderIndex < orderCount, 'GnosisProtocolRelayer: INVALID_ORDER');
require(!order.executed, 'GnosisProtocolRelayer: ORDER_EXECUTED');
require(oracleCreator.isOracleFinalized(order.oracleId) , 'GnosisProtocolRelayer: OBSERVATION_RUNNING');
require(block.timestamp <= order.deadline, 'GnosisProtocolRelayer: DEADLINE_REACHED');
order.executed = true;
/* Approve token on Gnosis Protocol */
TransferHelper.safeApprove(order.tokenIn, epochTokenLocker, order.tokenInAmount);
/* Deposit token in Gnosis Protocol */
IEpochTokenLocker(epochTokenLocker).deposit(order.tokenIn, order.tokenInAmount);
/* Lookup TokenIds in Gnosis Protocol */
uint16 sellToken = IBatchExchange(batchExchange).tokenAddressToIdMap(order.tokenIn);
uint16 buyToken = IBatchExchange(batchExchange).tokenAddressToIdMap(order.tokenOut);
uint256 expectedAmount = oracleCreator.consult(
order.oracleId,
order.tokenIn == address(0) ? WETH : order.tokenIn,
order.tokenInAmount
);
uint256 expectedAmountMin = expectedAmount.sub(expectedAmount.mul(order.priceTolerance) / PARTS_PER_MILLION);
uint256 expectedTokenOutAmount = order.tokenOutAmount;
require(
expectedAmountMin >= expectedTokenOutAmount.sub(expectedTokenOutAmount.mul(order.priceTolerance) / PARTS_PER_MILLION),
'GnosisProtocolRelayer: INVALID_PRICE_RANGE'
);
require(expectedAmountMin <= UINT128_MAX_VALUE,'GnosisProtocolRelayer: AMOUNT_OUT_OF_RANGE');
/* Calculate batch Deadline (5 Minutes window) */
uint32 validUntil = uint32(order.deadline/BATCH_TIME);
uint256 gpOrderId = IBatchExchange(batchExchange).placeOrder(buyToken, sellToken, validUntil, uint128(expectedAmountMin), order.tokenInAmount);
order.gpOrderId = gpOrderId;
emit PlacedTrade(orderIndex, gpOrderId);
}
function cancelOrder(uint256 orderIndex) external {
Order storage order = orders[orderIndex];
require(orderIndex < orderCount, 'GnosisProtocolRelayer: INVALID_ORDER');
require(msg.sender == owner, 'GnosisProtocolRelayer: CALLER_NOT_OWNER');
require(order.executed, 'GnosisProtocolRelayer: ORDER_NOT_EXECUTED');
uint16[] memory orderArray = new uint16[](1);
orderArray[0] = uint16(order.gpOrderId);
IBatchExchange(batchExchange).cancelOrders(orderArray);
}
// Updates a price oracle and sends a bounty to msg.sender
function updateOracle(uint256 orderIndex) external {
Order storage order = orders[orderIndex];
require(orderIndex < orderCount, 'GnosisProtocolRelayer: INVALID_ORDER');
require(block.timestamp <= order.deadline, 'GnosisProtocolRelayer: DEADLINE_REACHED');
require(!oracleCreator.isOracleFinalized(order.oracleId) , 'GnosisProtocolRelayer: OBSERVATION_ENDED');
uint256 amountBounty = GAS_ORACLE_UPDATE.mul(tx.gasprice).add(BOUNTY);
(uint reserve0, uint reserve1,) = IDXswapPair(order.oraclePair).getReserves();
address token0 = IDXswapPair(order.oraclePair).token0();
address tokenIn = order.tokenIn == address(0) ? WETH : order.tokenIn;
// Makes sure the reserve of TokenIn is higher then minReserve
if(tokenIn == token0){
require(
reserve0 >= order.minReserve,
'GnosisProtocolRelayer: RESERVE_TO_LOW'
);
} else {
require(
reserve1 >= order.minReserve,
'GnosisProtocolRelayer: RESERVE_TO_LOW'
);
}
oracleCreator.update(order.oracleId);
if(address(this).balance >= amountBounty){
TransferHelper.safeTransferETH(msg.sender, amountBounty);
}
}
function withdrawExpiredOrder(uint256 orderIndex) external {
Order storage order = orders[orderIndex];
require(orderIndex < orderCount, 'GnosisProtocolRelayer: INVALID_ORDER');
require(block.timestamp > order.deadline, 'GnosisProtocolRelayer: DEADLINE_NOT_REACHED');
require(!order.executed, 'GnosisProtocolRelayer: ORDER_EXECUTED');
if (order.tokenIn == WETH) {
IWETH(WETH).withdraw(order.tokenInAmount);
TransferHelper.safeTransferETH(owner, order.tokenInAmount);
} else {
TransferHelper.safeTransfer(order.tokenIn, owner, order.tokenInAmount);
}
order.executed = true;
emit WithdrawnExpiredOrder(orderIndex);
}
// Releases tokens from Gnosis Protocol
function withdrawToken(address token) public {
IEpochTokenLocker(epochTokenLocker).withdraw(address(this), token);
if (token == WETH) {
uint balance = IWETH(WETH).balanceOf(address(this));
IWETH(WETH).withdraw(balance);
}
}
// Internal function to return the pair address on a given factory
function _pair(address tokenA, address tokenB, address factory) internal view returns (address pair) {
require(exchangeFactoryWhitelist[factory], 'GnosisProtocolRelayer: INVALID_FACTORY');
pair = IDXswapFactory(factory).getPair(tokenA, tokenB);
}
// Returns an OrderIndex that is used to reference liquidity orders
function _OrderIndex() internal returns(uint256 orderIndex){
orderIndex = orderCount;
orderCount++;
}
// Allows the owner to withdraw any ERC20 from the relayer
function ERC20Withdraw(address token, uint256 amount) external {
require(msg.sender == owner, 'GnosisProtocolRelayer: CALLER_NOT_OWNER');
TransferHelper.safeTransfer(token, owner, amount);
}
// Allows the owner to withdraw any ETH amount from the relayer
function ETHWithdraw(uint256 amount) external {
require(msg.sender == owner, 'GnosisProtocolRelayer: CALLER_NOT_OWNER');
TransferHelper.safeTransferETH(owner, amount);
}
// Returns the data of one specific order
function GetOrderDetails(uint256 orderIndex) external view returns (Order memory) {
return orders[orderIndex];
}
receive() external payable {}
}

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_owner","internalType":"address payable"},{"type":"address","name":"_batchExchange","internalType":"address"},{"type":"address","name":"_epochTokenLocker","internalType":"address"},{"type":"address[]","name":"_factoryWhitelist","internalType":"address[]"},{"type":"address","name":"_WETH","internalType":"address"},{"type":"address","name":"_oracleCreater","internalType":"contract OracleCreator"}]},{"type":"event","name":"NewOrder","inputs":[{"type":"uint256","name":"_orderIndex","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"PlacedTrade","inputs":[{"type":"uint256","name":"_orderIndex","internalType":"uint256","indexed":true},{"type":"uint256","name":"_gpOrderID","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"WithdrawnExpiredOrder","inputs":[{"type":"uint256","name":"_orderIndex","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"BATCH_TIME","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"BOUNTY","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"ERC20Withdraw","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"ETHWithdraw","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"GAS_ORACLE_UPDATE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct GnosisProtocolRelayer.Order","components":[{"type":"address","name":"tokenIn","internalType":"address"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"uint128","name":"tokenInAmount","internalType":"uint128"},{"type":"uint128","name":"tokenOutAmount","internalType":"uint128"},{"type":"uint256","name":"priceTolerance","internalType":"uint256"},{"type":"uint256","name":"minReserve","internalType":"uint256"},{"type":"address","name":"oraclePair","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"uint256","name":"oracleId","internalType":"uint256"},{"type":"uint256","name":"gpOrderId","internalType":"uint256"},{"type":"address","name":"factory","internalType":"address"},{"type":"bool","name":"executed","internalType":"bool"}]}],"name":"GetOrderDetails","inputs":[{"type":"uint256","name":"orderIndex","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"ORACLE_WINDOW_TIME","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"PARTS_PER_MILLION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint128","name":"","internalType":"uint128"}],"name":"UINT128_MAX_VALUE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"UINT32_MAX_VALUE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"WETH","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"batchExchange","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancelOrder","inputs":[{"type":"uint256","name":"orderIndex","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"epochTokenLocker","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"exchangeFactoryWhitelist","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract OracleCreator"}],"name":"oracleCreator","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"orderCount","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"orderIndex","internalType":"uint256"}],"name":"orderTrade","inputs":[{"type":"address","name":"tokenIn","internalType":"address"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"uint128","name":"tokenInAmount","internalType":"uint128"},{"type":"uint128","name":"tokenOutAmount","internalType":"uint128"},{"type":"uint256","name":"priceTolerance","internalType":"uint256"},{"type":"uint256","name":"minReserve","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"address","name":"factory","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"tokenIn","internalType":"address"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"uint128","name":"tokenInAmount","internalType":"uint128"},{"type":"uint128","name":"tokenOutAmount","internalType":"uint128"},{"type":"uint256","name":"priceTolerance","internalType":"uint256"},{"type":"uint256","name":"minReserve","internalType":"uint256"},{"type":"address","name":"oraclePair","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"uint256","name":"oracleId","internalType":"uint256"},{"type":"uint256","name":"gpOrderId","internalType":"uint256"},{"type":"address","name":"factory","internalType":"address"},{"type":"bool","name":"executed","internalType":"bool"}],"name":"orders","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address payable"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"placeTrade","inputs":[{"type":"uint256","name":"orderIndex","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateOracle","inputs":[{"type":"uint256","name":"orderIndex","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdrawExpiredOrder","inputs":[{"type":"uint256","name":"orderIndex","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdrawToken","inputs":[{"type":"address","name":"token","internalType":"address"}]},{"type":"receive","stateMutability":"payable"}]
            

Contract Creation Code

0x6101e0604052620291ac608052620f424060a052662386f26fc1000060c052607860e0526001600160e01b0319610120526001600160801b0319610140523480156200004a57600080fd5b506040516200331c3803806200331c8339810160408190526200006d91620001ec565b60008351116200009a5760405162461bcd60e51b815260040162000091906200032e565b60405180910390fd5b606085811b6001600160601b03199081166101605285821b811661018052600080546001600160a01b0319166001600160a01b038581169190911790915588831b82166101a0529184901b166101c0526040805163724600af60e11b815290519186169163e48c015e91600480820192602092909190829003018186803b1580156200012557600080fd5b505afa1580156200013a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000160919062000301565b60e01b6001600160e01b0319166101005260005b8351811015620001cc576001600360008684815181106200019157fe5b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff191691151591909117905560010162000174565b50505050505050620003de565b8051620001e681620003c5565b92915050565b60008060008060008060c0878903121562000205578182fd5b86516200021281620003c5565b809650506020808801516200022781620003c5565b60408901519096506200023a81620003c5565b60608901519095506001600160401b0381111562000256578384fd5b8089018a601f82011262000268578485fd5b80519150620002816200027b83620003a5565b6200037e565b82815283810190828501858502840186018e10156200029e578788fd5b8793505b84841015620002cc57620002b78e82620001d9565b835260019390930192918501918501620002a2565b508097505050505050620002e48860808901620001d9565b9150620002f58860a08901620001d9565b90509295509295509295565b60006020828403121562000313578081fd5b815163ffffffff8116811462000327578182fd5b9392505050565b60208082526030908201527f476e6f73697350726f746f636f6c52656c617965723a204d495353494e475f4660408201526f1050d513d49657d5d2125511531254d560821b606082015260800190565b6040518181016001600160401b03811182821017156200039d57600080fd5b604052919050565b60006001600160401b03821115620003bb578081fd5b5060209081020190565b6001600160a01b0381168114620003db57600080fd5b50565b60805160a05160c05160e0516101005160e01c6101205160e01c6101405160801c6101605160601c6101805160601c6101a05160601c6101c05160601c612e03620005196000398061051e528061056c5280610a495280610a6c5280610afc5280610f985280610fea528061108b52806111bd528061148a52806119b45250806105e65280610630528061070052806108bd528061111d528061159452806115d65280611cc15280611d045250806104565280610f2d528061176952806117b55250806107c65280610834528061184552806118ef5280611bd15250806104325280611b305250806106a452806109ac525080611b7b5280611d2f5250806108585280610e2e5250806112d952806115fd52508061096c5280611a5e5280611ace5280611d535250806110f952806112fd5250612e036000f3fe60806040526004361061014f5760003560e01c80638c0a63bf116100b6578063d081f2b81161006f578063d081f2b814610384578063d41bee5614610399578063d530f4e7146103b9578063e48c015e146103d9578063ec6ea812146103ee578063ffc5ea0d1461040357610156565b80638c0a63bf146102cd5780638da5cb5b146102e2578063a85c38ef146102f7578063ad5c46481461032f578063c23c1c9f14610344578063c58125bc1461036457610156565b8063514fcac711610108578063514fcac71461023b578063607695511461025b57806378f75317146102705780637a3feb38146102855780638570c8a91461029857806389476069146102ad57610156565b8063096563651461015b5780630b34538e146101865780631373f170146101a85780632453ffa8146101d55780632cb2db61146101f75780633e93e2011461021957610156565b3661015657005b600080fd5b34801561016757600080fd5b50610170610430565b60405161017d9190612ce4565b60405180910390f35b34801561019257600080fd5b5061019b610454565b60405161017d919061246f565b3480156101b457600080fd5b506101c86101c3366004612259565b610478565b60405161017d919061258f565b3480156101e157600080fd5b506101ea61048d565b60405161017d9190612d35565b34801561020357600080fd5b506102176102123660046123e6565b610493565b005b34801561022557600080fd5b5061022e6106a2565b60405161017d9190612d7d565b34801561024757600080fd5b506102176102563660046123e6565b6106c6565b34801561026757600080fd5b5061019b610832565b34801561027c57600080fd5b506101ea610856565b6101ea610293366004612298565b61087a565b3480156102a457600080fd5b5061019b610f07565b3480156102b957600080fd5b506102176102c8366004612259565b610f16565b3480156102d957600080fd5b506101ea6110f7565b3480156102ee57600080fd5b5061019b61111b565b34801561030357600080fd5b506103176103123660046123e6565b61113f565b60405161017d9c9b9a9998979695949392919061249d565b34801561033b57600080fd5b5061019b6111bb565b34801561035057600080fd5b5061021761035f3660046123e6565b6111df565b34801561037057600080fd5b5061021761037f3660046123e6565b611589565b34801561039057600080fd5b506101ea6115fb565b3480156103a557600080fd5b506102176103b43660046123e6565b61161f565b3480156103c557600080fd5b506102176103d4366004612325565b611cb6565b3480156103e557600080fd5b5061022e611d2d565b3480156103fa57600080fd5b506101ea611d51565b34801561040f57600080fd5b5061042361041e3660046123e6565b611d75565b60405161017d9190612c20565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b60036020526000908152604090205460ff1681565b60015481565b600081815260026020526040902060015482106104cb5760405162461bcd60e51b81526004016104c290612b4f565b60405180910390fd5b806006015442116104ee5760405162461bcd60e51b81526004016104c2906127af565b6009810154600160a01b900460ff161561051a5760405162461bcd60e51b81526004016104c2906128d3565b80547f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161415610619576002810154604051632e1a7d4d60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691632e1a7d4d916105a9916001600160801b031690600401612ce4565b600060405180830381600087803b1580156105c357600080fd5b505af11580156105d7573d6000803e3d6000fd5b505050600282015461061491507f0000000000000000000000000000000000000000000000000000000000000000906001600160801b0316611e40565b61065e565b8054600282015461065e916001600160a01b0316907f0000000000000000000000000000000000000000000000000000000000000000906001600160801b0316611ed2565b60098101805460ff60a01b1916600160a01b17905560405182907f3f9d01b8340245ea975087559a7911155f1b300f5b19aa35fcc67d4adf962c8590600090a25050565b7f000000000000000000000000000000000000000000000000000000000000000081565b600081815260026020526040902060015482106106f55760405162461bcd60e51b81526004016104c290612b4f565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461073d5760405162461bcd60e51b81526004016104c29061264a565b6009810154600160a01b900460ff166107685760405162461bcd60e51b81526004016104c290612766565b6040805160018082528183019092526060916020808301908036833701905050905081600801548160008151811061079c57fe5b61ffff909216602092830291909101909101526040516312f706d360e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690634bdc1b4c906107fb908490600401612547565b600060405180830381600087803b15801561081557600080fd5b505af1158015610829573d6000803e3d6000fd5b50505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b6001600160a01b03811660009081526003602052604081205460ff166108b25760405162461bcd60e51b81526004016104c2906129a7565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146108fa5760405162461bcd60e51b81526004016104c29061264a565b876001600160a01b0316896001600160a01b0316141561092c5760405162461bcd60e51b81526004016104c290612b0c565b6000876001600160801b031611801561094e57506000866001600160801b0316115b61096a5760405162461bcd60e51b81526004016104c290612a7c565b7f00000000000000000000000000000000000000000000000000000000000000008511156109aa5760405162461bcd60e51b81526004016104c2906127fa565b7f000000000000000000000000000000000000000000000000000000000000000063ffffffff168311156109f05760405162461bcd60e51b81526004016104c290612a35565b82421115610a105760405162461bcd60e51b81526004016104c29061288c565b6001600160a01b038916610aec57866001600160801b0316341015610a475760405162461bcd60e51b81526004016104c290612ac7565b7f000000000000000000000000000000000000000000000000000000000000000098507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0886001600160801b03166040518263ffffffff1660e01b81526004016000604051808303818588803b158015610ace57600080fd5b505af1158015610ae2573d6000803e3d6000fd5b5050505050610b1e565b6001600160a01b038816610b1e577f000000000000000000000000000000000000000000000000000000000000000097505b6040516370a0823160e01b81526001600160801b038816906001600160a01b038b16906370a0823190610b5590309060040161246f565b60206040518083038186803b158015610b6d57600080fd5b505afa158015610b81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba591906123fe565b1015610bc35760405162461bcd60e51b81526004016104c290612bd6565b6000610bd08a8a85611fc0565b90506001600160a01b038116610bf85760405162461bcd60e51b81526004016104c290612724565b610c0061207e565b91506040518061018001604052808b6001600160a01b031681526020018a6001600160a01b03168152602001896001600160801b03168152602001886001600160801b03168152602001878152602001868152602001826001600160a01b031681526020018581526020016000815260200160008152602001846001600160a01b03168152602001600015158152506002600084815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060408201518160020160006101000a8154816001600160801b0302191690836001600160801b0316021790555060608201518160020160106101000a8154816001600160801b0302191690836001600160801b031602179055506080820151816003015560a0820151816004015560c08201518160050160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060e08201518160060155610100820151816007015561012082015181600801556101408201518160090160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506101608201518160090160146101000a81548160ff0219169083151502179055509050506000809054906101000a90046001600160a01b03166001600160a01b03166306649d677f0000000000000000000000000000000000000000000000000000000000000000836040518363ffffffff1660e01b8152600401610e6b929190612d3e565b602060405180830381600087803b158015610e8557600080fd5b505af1158015610e99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ebd91906123fe565b60008381526002602052604080822060070192909255905183917fd56de72b04be4cf70c2bab5e6905fb51e28beb22f7a5d40a6dc4103149d6110691a25098975050505050505050565b6000546001600160a01b031681565b60405163f940e38560e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063f940e38590610f649030908590600401612483565b600060405180830381600087803b158015610f7e57600080fd5b505af1158015610f92573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031614156110f4576040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a082319061101f90309060040161246f565b602060405180830381600087803b15801561103957600080fd5b505af115801561104d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061107191906123fe565b604051632e1a7d4d60e01b81529091506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632e1a7d4d906110c0908490600401612d35565b600060405180830381600087803b1580156110da57600080fd5b505af11580156110ee573d6000803e3d6000fd5b50505050505b50565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b600260208190526000918252604090912080546001820154928201546003830154600484015460058501546006860154600787015460088801546009909801546001600160a01b03978816998816986001600160801b0380891699600160801b909904169794851694909190811690600160a01b900460ff168c565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000818152600260205260409020600154821061120e5760405162461bcd60e51b81526004016104c290612b4f565b80600601544211156112325760405162461bcd60e51b81526004016104c29061288c565b60005460078201546040516325fa700360e01b81526001600160a01b03909216916325fa70039161126591600401612d35565b60206040518083038186803b15801561127d57600080fd5b505afa158015611291573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112b59190612350565b156112d25760405162461bcd60e51b81526004016104c2906129ed565b60006113347f00000000000000000000000000000000000000000000000000000000000000006113287f00000000000000000000000000000000000000000000000000000000000000003a63ffffffff61208b16565b9063ffffffff6120c816565b90506000808360050160009054906101000a90046001600160a01b03166001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561138957600080fd5b505afa15801561139d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c19190612370565b506001600160701b031691506001600160701b0316915060008460050160009054906101000a90046001600160a01b03166001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b15801561142a57600080fd5b505afa15801561143e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611462919061227c565b85549091506000906001600160a01b0316156114885785546001600160a01b03166114aa565b7f00000000000000000000000000000000000000000000000000000000000000005b9050816001600160a01b0316816001600160a01b031614156114ef5785600401548410156114ea5760405162461bcd60e51b81526004016104c290612918565b611513565b85600401548310156115135760405162461bcd60e51b81526004016104c290612918565b6000546007870154604051634155c48560e11b81526001600160a01b03909216916382ab890a9161154691600401612d35565b600060405180830381600087803b15801561156057600080fd5b505af1158015611574573d6000803e3d6000fd5b50505050844710610829576108293386611e40565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146115d15760405162461bcd60e51b81526004016104c29061264a565b6110f47f000000000000000000000000000000000000000000000000000000000000000082611e40565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000818152600260205260409020600154821061164e5760405162461bcd60e51b81526004016104c290612b4f565b6009810154600160a01b900460ff161561167a5760405162461bcd60e51b81526004016104c2906128d3565b60005460078201546040516325fa700360e01b81526001600160a01b03909216916325fa7003916116ad91600401612d35565b60206040518083038186803b1580156116c557600080fd5b505afa1580156116d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116fd9190612350565b6117195760405162461bcd60e51b81526004016104c29061295d565b806006015442111561173d5760405162461bcd60e51b81526004016104c29061288c565b60098101805460ff60a01b1916600160a01b17905580546002820154611797916001600160a01b0316907f0000000000000000000000000000000000000000000000000000000000000000906001600160801b03166120eb565b805460028201546040516311f9fbc960e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116936347e7ef24936117f89391909216916001600160801b03169060040161250c565b600060405180830381600087803b15801561181257600080fd5b505af1158015611826573d6000803e3d6000fd5b5050825460405163ef574d2360e01b8152600093506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116935063ef574d239261187d9291169060040161246f565b60206040518083038186803b15801561189557600080fd5b505afa1580156118a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118cd91906123c4565b600183015460405163ef574d2360e01b81529192506000916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169263ef574d239261192892919091169060040161246f565b60206040518083038186803b15801561194057600080fd5b505afa158015611954573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061197891906123c4565b600080546007860154865493945091926001600160a01b0391821692634023282b92909116156119b25786546001600160a01b03166119d4565b7f00000000000000000000000000000000000000000000000000000000000000005b60028801546040516001600160e01b031960e086901b168152611a059392916001600160801b031690600401612d55565b60206040518083038186803b158015611a1d57600080fd5b505afa158015611a31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a5591906123fe565b90506000611aaa7f0000000000000000000000000000000000000000000000000000000000000000611a9487600301548561208b90919063ffffffff16565b81611a9b57fe5b8491900463ffffffff6121d216565b60028601546003870154919250600160801b90046001600160801b031690611b0f907f000000000000000000000000000000000000000000000000000000000000000090611af990849061208b565b81611b0057fe5b8391900463ffffffff6121d216565b821015611b2e5760405162461bcd60e51b81526004016104c290612600565b7f00000000000000000000000000000000000000000000000000000000000000006001600160801b0316821115611b775760405162461bcd60e51b81526004016104c290612842565b60007f000000000000000000000000000000000000000000000000000000000000000063ffffffff16876006015481611bac57fe5b60028901546040516309b0f4e560e21b81529290910492506000916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916326c3d39491611c16918a918c9188918b916001600160801b031690600401612cf8565b602060405180830381600087803b158015611c3057600080fd5b505af1158015611c44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c6891906123fe565b9050808860080181905550887f38f1372ae30d4273e14ea8b395321dc1e4d5fcad60cd0785b0b34ef99b74f2a182604051611ca39190612d35565b60405180910390a2505050505050505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611cfe5760405162461bcd60e51b81526004016104c29061264a565b611d29827f000000000000000000000000000000000000000000000000000000000000000083611ed2565b5050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b611d7d6121f5565b5060009081526002602081815260409283902083516101808101855281546001600160a01b0390811682526001830154811693820193909352928101546001600160801b0380821695850195909552600160801b9004909316606083015260038301546080830152600483015460a08301526005830154811660c0830152600683015460e083015260078301546101008301526008830154610120830152600990920154918216610140820152600160a01b90910460ff16151561016082015290565b604080516000808252602082019092526001600160a01b038416908390604051611e6a9190612436565b60006040518083038185875af1925050503d8060008114611ea7576040519150601f19603f3d011682016040523d82523d6000602084013e611eac565b606091505b5050905080611ecd5760405162461bcd60e51b81526004016104c290612b93565b505050565b60006060846001600160a01b031663a9059cbb8585604051602401611ef892919061252e565b6040516020818303038152906040529060e01b6020820180516001600160e01b038381831617835250505050604051611f319190612436565b6000604051808303816000865af19150503d8060008114611f6e576040519150601f19603f3d011682016040523d82523d6000602084013e611f73565b606091505b5091509150818015611f9d575080511580611f9d575080806020019051810190611f9d9190612350565b611fb95760405162461bcd60e51b81526004016104c2906125c9565b5050505050565b6001600160a01b03811660009081526003602052604081205460ff16611ff85760405162461bcd60e51b81526004016104c2906129a7565b60405163e6a4390560e01b81526001600160a01b0383169063e6a43905906120269087908790600401612483565b60206040518083038186803b15801561203e57600080fd5b505afa158015612052573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612076919061227c565b949350505050565b6001805480820190915590565b60008115806120a6575050808202828282816120a357fe5b04145b6120c25760405162461bcd60e51b81526004016104c290612691565b92915050565b808201828110156120c25760405162461bcd60e51b81526004016104c2906126bf565b60006060846001600160a01b031663095ea7b3858560405160240161211192919061252e565b6040516020818303038152906040529060e01b6020820180516001600160e01b03838183161783525050505060405161214a9190612436565b6000604051808303816000865af19150503d8060008114612187576040519150601f19603f3d011682016040523d82523d6000602084013e61218c565b606091505b50915091508180156121b65750805115806121b65750808060200190518101906121b69190612350565b611fb95760405162461bcd60e51b81526004016104c2906126ed565b808203828111156120c25760405162461bcd60e51b81526004016104c29061259a565b6040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810182905261016081019190915290565b60006020828403121561226a578081fd5b813561227581612d8e565b9392505050565b60006020828403121561228d578081fd5b815161227581612d8e565b600080600080600080600080610100898b0312156122b4578384fd5b88356122bf81612d8e565b975060208901356122cf81612d8e565b965060408901356122df81612db8565b955060608901356122ef81612db8565b94506080890135935060a0890135925060c0890135915060e089013561231481612d8e565b809150509295985092959890939650565b60008060408385031215612337578182fd5b823561234281612d8e565b946020939093013593505050565b600060208284031215612361578081fd5b81518015158114612275578182fd5b600080600060608486031215612384578283fd5b835161238f81612da3565b60208501519093506123a081612da3565b604085015190925063ffffffff811681146123b9578182fd5b809150509250925092565b6000602082840312156123d5578081fd5b815161ffff81168114612275578182fd5b6000602082840312156123f7578081fd5b5035919050565b60006020828403121561240f578081fd5b5051919050565b6001600160a01b03169052565b15159052565b6001600160801b03169052565b60008251815b81811015612456576020818601810151858301520161243c565b818111156124645782828501525b509190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039c8d1681529a8c1660208c01526001600160801b03998a1660408c01529790981660608a0152608089019590955260a088019390935290871660c087015260e086015261010085015261012084015292166101408201529015156101608201526101800190565b6001600160a01b039290921682526001600160801b0316602082015260400190565b6001600160a01b03929092168252602082015260400190565b6020808252825182820181905260009190848201906040850190845b8181101561258357835161ffff1683529284019291840191600101612563565b50909695505050505050565b901515815260200190565b60208082526015908201527464732d6d6174682d7375622d756e646572666c6f7760581b604082015260600190565b6020808252601f908201527f5472616e7366657248656c7065723a205452414e534645525f4641494c454400604082015260600190565b6020808252602a908201527f476e6f73697350726f746f636f6c52656c617965723a20494e56414c49445f50604082015269524943455f52414e474560b01b606082015260800190565b60208082526027908201527f476e6f73697350726f746f636f6c52656c617965723a2043414c4c45525f4e4f6040820152662a2fa7aba722a960c91b606082015260800190565b60208082526014908201527364732d6d6174682d6d756c2d6f766572666c6f7760601b604082015260600190565b60208082526014908201527364732d6d6174682d6164642d6f766572666c6f7760601b604082015260600190565b6020808252601e908201527f5472616e7366657248656c7065723a20415050524f56455f4641494c45440000604082015260600190565b60208082526022908201527f476e6f73697350726f746f636f6c52656c617965723a20554e4b4f574e5f504160408201526124a960f11b606082015260800190565b60208082526029908201527f476e6f73697350726f746f636f6c52656c617965723a204f524445525f4e4f5460408201526817d1561150d555115160ba1b606082015260800190565b6020808252602b908201527f476e6f73697350726f746f636f6c52656c617965723a20444541444c494e455f60408201526a1393d517d4915050d2115160aa1b606082015260800190565b60208082526028908201527f476e6f73697350726f746f636f6c52656c617965723a20494e56414c49445f546040820152674f4c4552414e434560c01b606082015260800190565b6020808252602a908201527f476e6f73697350726f746f636f6c52656c617965723a20414d4f554e545f4f55604082015269545f4f465f52414e474560b01b606082015260800190565b60208082526027908201527f476e6f73697350726f746f636f6c52656c617965723a20444541444c494e455f60408201526614915050d2115160ca1b606082015260800190565b60208082526025908201527f476e6f73697350726f746f636f6c52656c617965723a204f524445525f45584560408201526410d555115160da1b606082015260800190565b60208082526025908201527f476e6f73697350726f746f636f6c52656c617965723a20524553455256455f546040820152644f5f4c4f5760d81b606082015260800190565b6020808252602a908201527f476e6f73697350726f746f636f6c52656c617965723a204f42534552564154496040820152694f4e5f52554e4e494e4760b01b606082015260800190565b60208082526026908201527f476e6f73697350726f746f636f6c52656c617965723a20494e56414c49445f466040820152654143544f525960d01b606082015260800190565b60208082526028908201527f476e6f73697350726f746f636f6c52656c617965723a204f425345525641544960408201526713d397d15391115160c21b606082015260800190565b60208082526027908201527f476e6f73697350726f746f636f6c52656c617965723a20494e56414c49445f446040820152664541444c494e4560c81b606082015260800190565b6020808252602b908201527f476e6f73697350726f746f636f6c52656c617965723a20494e56414c49445f5460408201526a13d2d15397d05353d5539560aa1b606082015260800190565b60208082526025908201527f476e6f73697350726f746f636f6c52656c617965723a20494e5355464649454e6040820152640a8be8aa8960db1b606082015260800190565b60208082526023908201527f476e6f73697350726f746f636f6c52656c617965723a20494e56414c49445f5060408201526220a4a960e91b606082015260800190565b60208082526024908201527f476e6f73697350726f746f636f6c52656c617965723a20494e56414c49445f4f604082015263292222a960e11b606082015260800190565b60208082526023908201527f5472616e7366657248656c7065723a204554485f5452414e534645525f46414960408201526213115160ea1b606082015260800190565b6020808252602a908201527f476e6f73697350726f746f636f6c52656c617965723a20494e5355464649454e6040820152692a2faa27a5a2a72fa4a760b11b606082015260800190565b600061018082019050612c34828451612416565b6020830151612c466020840182612416565b506040830151612c596040840182612429565b506060830151612c6c6060840182612429565b506080830151608083015260a083015160a083015260c0830151612c9360c0840182612416565b5060e083810151908301526101008084015190830152610120808401519083015261014080840151612cc782850182612416565b505061016080840151612cdc82850182612423565b505092915050565b6001600160801b0391909116815260200190565b61ffff958616815293909416602084015263ffffffff9190911660408301526001600160801b039081166060830152909116608082015260a00190565b90815260200190565b9182526001600160a01b0316602082015260400190565b9283526001600160a01b039190911660208301526001600160801b0316604082015260600190565b63ffffffff91909116815260200190565b6001600160a01b03811681146110f457600080fd5b6001600160701b03811681146110f457600080fd5b6001600160801b03811681146110f457600080fdfea26469706673582212203da53e99b923809ac33371e120e9afc1422099da2e25198361b809b5b41874a664736f6c63430006060033000000000000000000000000e716ec63c5673b3a4732d22909b38d779fa47c3f00000000000000000000000025b06305cc4ec6afcf3e7c0b673da1ef8ae2631300000000000000000000000025b06305cc4ec6afcf3e7c0b673da1ef8ae2631300000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000e91d153e0b41518a2ce8dd3d7944fa863463a97d00000000000000000000000043b7b9a571e6b578f8e0e2e7ba05f960c40cb0f00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a818b4f111ccac7aa31d0bcc0806d64f2e0737d7

Deployed ByteCode

0x60806040526004361061014f5760003560e01c80638c0a63bf116100b6578063d081f2b81161006f578063d081f2b814610384578063d41bee5614610399578063d530f4e7146103b9578063e48c015e146103d9578063ec6ea812146103ee578063ffc5ea0d1461040357610156565b80638c0a63bf146102cd5780638da5cb5b146102e2578063a85c38ef146102f7578063ad5c46481461032f578063c23c1c9f14610344578063c58125bc1461036457610156565b8063514fcac711610108578063514fcac71461023b578063607695511461025b57806378f75317146102705780637a3feb38146102855780638570c8a91461029857806389476069146102ad57610156565b8063096563651461015b5780630b34538e146101865780631373f170146101a85780632453ffa8146101d55780632cb2db61146101f75780633e93e2011461021957610156565b3661015657005b600080fd5b34801561016757600080fd5b50610170610430565b60405161017d9190612ce4565b60405180910390f35b34801561019257600080fd5b5061019b610454565b60405161017d919061246f565b3480156101b457600080fd5b506101c86101c3366004612259565b610478565b60405161017d919061258f565b3480156101e157600080fd5b506101ea61048d565b60405161017d9190612d35565b34801561020357600080fd5b506102176102123660046123e6565b610493565b005b34801561022557600080fd5b5061022e6106a2565b60405161017d9190612d7d565b34801561024757600080fd5b506102176102563660046123e6565b6106c6565b34801561026757600080fd5b5061019b610832565b34801561027c57600080fd5b506101ea610856565b6101ea610293366004612298565b61087a565b3480156102a457600080fd5b5061019b610f07565b3480156102b957600080fd5b506102176102c8366004612259565b610f16565b3480156102d957600080fd5b506101ea6110f7565b3480156102ee57600080fd5b5061019b61111b565b34801561030357600080fd5b506103176103123660046123e6565b61113f565b60405161017d9c9b9a9998979695949392919061249d565b34801561033b57600080fd5b5061019b6111bb565b34801561035057600080fd5b5061021761035f3660046123e6565b6111df565b34801561037057600080fd5b5061021761037f3660046123e6565b611589565b34801561039057600080fd5b506101ea6115fb565b3480156103a557600080fd5b506102176103b43660046123e6565b61161f565b3480156103c557600080fd5b506102176103d4366004612325565b611cb6565b3480156103e557600080fd5b5061022e611d2d565b3480156103fa57600080fd5b506101ea611d51565b34801561040f57600080fd5b5061042361041e3660046123e6565b611d75565b60405161017d9190612c20565b7f00000000000000000000000000000000ffffffffffffffffffffffffffffffff81565b7f00000000000000000000000025b06305cc4ec6afcf3e7c0b673da1ef8ae2631381565b60036020526000908152604090205460ff1681565b60015481565b600081815260026020526040902060015482106104cb5760405162461bcd60e51b81526004016104c290612b4f565b60405180910390fd5b806006015442116104ee5760405162461bcd60e51b81526004016104c2906127af565b6009810154600160a01b900460ff161561051a5760405162461bcd60e51b81526004016104c2906128d3565b80547f000000000000000000000000e91d153e0b41518a2ce8dd3d7944fa863463a97d6001600160a01b0390811691161415610619576002810154604051632e1a7d4d60e01b81526001600160a01b037f000000000000000000000000e91d153e0b41518a2ce8dd3d7944fa863463a97d1691632e1a7d4d916105a9916001600160801b031690600401612ce4565b600060405180830381600087803b1580156105c357600080fd5b505af11580156105d7573d6000803e3d6000fd5b505050600282015461061491507f000000000000000000000000e716ec63c5673b3a4732d22909b38d779fa47c3f906001600160801b0316611e40565b61065e565b8054600282015461065e916001600160a01b0316907f000000000000000000000000e716ec63c5673b3a4732d22909b38d779fa47c3f906001600160801b0316611ed2565b60098101805460ff60a01b1916600160a01b17905560405182907f3f9d01b8340245ea975087559a7911155f1b300f5b19aa35fcc67d4adf962c8590600090a25050565b7f00000000000000000000000000000000000000000000000000000000ffffffff81565b600081815260026020526040902060015482106106f55760405162461bcd60e51b81526004016104c290612b4f565b336001600160a01b037f000000000000000000000000e716ec63c5673b3a4732d22909b38d779fa47c3f161461073d5760405162461bcd60e51b81526004016104c29061264a565b6009810154600160a01b900460ff166107685760405162461bcd60e51b81526004016104c290612766565b6040805160018082528183019092526060916020808301908036833701905050905081600801548160008151811061079c57fe5b61ffff909216602092830291909101909101526040516312f706d360e21b81526001600160a01b037f00000000000000000000000025b06305cc4ec6afcf3e7c0b673da1ef8ae263131690634bdc1b4c906107fb908490600401612547565b600060405180830381600087803b15801561081557600080fd5b505af1158015610829573d6000803e3d6000fd5b50505050505050565b7f00000000000000000000000025b06305cc4ec6afcf3e7c0b673da1ef8ae2631381565b7f000000000000000000000000000000000000000000000000000000000000007881565b6001600160a01b03811660009081526003602052604081205460ff166108b25760405162461bcd60e51b81526004016104c2906129a7565b336001600160a01b037f000000000000000000000000e716ec63c5673b3a4732d22909b38d779fa47c3f16146108fa5760405162461bcd60e51b81526004016104c29061264a565b876001600160a01b0316896001600160a01b0316141561092c5760405162461bcd60e51b81526004016104c290612b0c565b6000876001600160801b031611801561094e57506000866001600160801b0316115b61096a5760405162461bcd60e51b81526004016104c290612a7c565b7f00000000000000000000000000000000000000000000000000000000000f42408511156109aa5760405162461bcd60e51b81526004016104c2906127fa565b7f00000000000000000000000000000000000000000000000000000000ffffffff63ffffffff168311156109f05760405162461bcd60e51b81526004016104c290612a35565b82421115610a105760405162461bcd60e51b81526004016104c29061288c565b6001600160a01b038916610aec57866001600160801b0316341015610a475760405162461bcd60e51b81526004016104c290612ac7565b7f000000000000000000000000e91d153e0b41518a2ce8dd3d7944fa863463a97d98507f000000000000000000000000e91d153e0b41518a2ce8dd3d7944fa863463a97d6001600160a01b031663d0e30db0886001600160801b03166040518263ffffffff1660e01b81526004016000604051808303818588803b158015610ace57600080fd5b505af1158015610ae2573d6000803e3d6000fd5b5050505050610b1e565b6001600160a01b038816610b1e577f000000000000000000000000e91d153e0b41518a2ce8dd3d7944fa863463a97d97505b6040516370a0823160e01b81526001600160801b038816906001600160a01b038b16906370a0823190610b5590309060040161246f565b60206040518083038186803b158015610b6d57600080fd5b505afa158015610b81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba591906123fe565b1015610bc35760405162461bcd60e51b81526004016104c290612bd6565b6000610bd08a8a85611fc0565b90506001600160a01b038116610bf85760405162461bcd60e51b81526004016104c290612724565b610c0061207e565b91506040518061018001604052808b6001600160a01b031681526020018a6001600160a01b03168152602001896001600160801b03168152602001886001600160801b03168152602001878152602001868152602001826001600160a01b031681526020018581526020016000815260200160008152602001846001600160a01b03168152602001600015158152506002600084815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060408201518160020160006101000a8154816001600160801b0302191690836001600160801b0316021790555060608201518160020160106101000a8154816001600160801b0302191690836001600160801b031602179055506080820151816003015560a0820151816004015560c08201518160050160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060e08201518160060155610100820151816007015561012082015181600801556101408201518160090160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506101608201518160090160146101000a81548160ff0219169083151502179055509050506000809054906101000a90046001600160a01b03166001600160a01b03166306649d677f0000000000000000000000000000000000000000000000000000000000000078836040518363ffffffff1660e01b8152600401610e6b929190612d3e565b602060405180830381600087803b158015610e8557600080fd5b505af1158015610e99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ebd91906123fe565b60008381526002602052604080822060070192909255905183917fd56de72b04be4cf70c2bab5e6905fb51e28beb22f7a5d40a6dc4103149d6110691a25098975050505050505050565b6000546001600160a01b031681565b60405163f940e38560e01b81526001600160a01b037f00000000000000000000000025b06305cc4ec6afcf3e7c0b673da1ef8ae26313169063f940e38590610f649030908590600401612483565b600060405180830381600087803b158015610f7e57600080fd5b505af1158015610f92573d6000803e3d6000fd5b505050507f000000000000000000000000e91d153e0b41518a2ce8dd3d7944fa863463a97d6001600160a01b0316816001600160a01b031614156110f4576040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000e91d153e0b41518a2ce8dd3d7944fa863463a97d16906370a082319061101f90309060040161246f565b602060405180830381600087803b15801561103957600080fd5b505af115801561104d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061107191906123fe565b604051632e1a7d4d60e01b81529091506001600160a01b037f000000000000000000000000e91d153e0b41518a2ce8dd3d7944fa863463a97d1690632e1a7d4d906110c0908490600401612d35565b600060405180830381600087803b1580156110da57600080fd5b505af11580156110ee573d6000803e3d6000fd5b50505050505b50565b7f00000000000000000000000000000000000000000000000000000000000291ac81565b7f000000000000000000000000e716ec63c5673b3a4732d22909b38d779fa47c3f81565b600260208190526000918252604090912080546001820154928201546003830154600484015460058501546006860154600787015460088801546009909801546001600160a01b03978816998816986001600160801b0380891699600160801b909904169794851694909190811690600160a01b900460ff168c565b7f000000000000000000000000e91d153e0b41518a2ce8dd3d7944fa863463a97d81565b6000818152600260205260409020600154821061120e5760405162461bcd60e51b81526004016104c290612b4f565b80600601544211156112325760405162461bcd60e51b81526004016104c29061288c565b60005460078201546040516325fa700360e01b81526001600160a01b03909216916325fa70039161126591600401612d35565b60206040518083038186803b15801561127d57600080fd5b505afa158015611291573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112b59190612350565b156112d25760405162461bcd60e51b81526004016104c2906129ed565b60006113347f000000000000000000000000000000000000000000000000002386f26fc100006113287f00000000000000000000000000000000000000000000000000000000000291ac3a63ffffffff61208b16565b9063ffffffff6120c816565b90506000808360050160009054906101000a90046001600160a01b03166001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561138957600080fd5b505afa15801561139d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c19190612370565b506001600160701b031691506001600160701b0316915060008460050160009054906101000a90046001600160a01b03166001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b15801561142a57600080fd5b505afa15801561143e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611462919061227c565b85549091506000906001600160a01b0316156114885785546001600160a01b03166114aa565b7f000000000000000000000000e91d153e0b41518a2ce8dd3d7944fa863463a97d5b9050816001600160a01b0316816001600160a01b031614156114ef5785600401548410156114ea5760405162461bcd60e51b81526004016104c290612918565b611513565b85600401548310156115135760405162461bcd60e51b81526004016104c290612918565b6000546007870154604051634155c48560e11b81526001600160a01b03909216916382ab890a9161154691600401612d35565b600060405180830381600087803b15801561156057600080fd5b505af1158015611574573d6000803e3d6000fd5b50505050844710610829576108293386611e40565b336001600160a01b037f000000000000000000000000e716ec63c5673b3a4732d22909b38d779fa47c3f16146115d15760405162461bcd60e51b81526004016104c29061264a565b6110f47f000000000000000000000000e716ec63c5673b3a4732d22909b38d779fa47c3f82611e40565b7f000000000000000000000000000000000000000000000000002386f26fc1000081565b6000818152600260205260409020600154821061164e5760405162461bcd60e51b81526004016104c290612b4f565b6009810154600160a01b900460ff161561167a5760405162461bcd60e51b81526004016104c2906128d3565b60005460078201546040516325fa700360e01b81526001600160a01b03909216916325fa7003916116ad91600401612d35565b60206040518083038186803b1580156116c557600080fd5b505afa1580156116d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116fd9190612350565b6117195760405162461bcd60e51b81526004016104c29061295d565b806006015442111561173d5760405162461bcd60e51b81526004016104c29061288c565b60098101805460ff60a01b1916600160a01b17905580546002820154611797916001600160a01b0316907f00000000000000000000000025b06305cc4ec6afcf3e7c0b673da1ef8ae26313906001600160801b03166120eb565b805460028201546040516311f9fbc960e21b81526001600160a01b037f00000000000000000000000025b06305cc4ec6afcf3e7c0b673da1ef8ae263138116936347e7ef24936117f89391909216916001600160801b03169060040161250c565b600060405180830381600087803b15801561181257600080fd5b505af1158015611826573d6000803e3d6000fd5b5050825460405163ef574d2360e01b8152600093506001600160a01b037f00000000000000000000000025b06305cc4ec6afcf3e7c0b673da1ef8ae263138116935063ef574d239261187d9291169060040161246f565b60206040518083038186803b15801561189557600080fd5b505afa1580156118a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118cd91906123c4565b600183015460405163ef574d2360e01b81529192506000916001600160a01b037f00000000000000000000000025b06305cc4ec6afcf3e7c0b673da1ef8ae2631381169263ef574d239261192892919091169060040161246f565b60206040518083038186803b15801561194057600080fd5b505afa158015611954573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061197891906123c4565b600080546007860154865493945091926001600160a01b0391821692634023282b92909116156119b25786546001600160a01b03166119d4565b7f000000000000000000000000e91d153e0b41518a2ce8dd3d7944fa863463a97d5b60028801546040516001600160e01b031960e086901b168152611a059392916001600160801b031690600401612d55565b60206040518083038186803b158015611a1d57600080fd5b505afa158015611a31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a5591906123fe565b90506000611aaa7f00000000000000000000000000000000000000000000000000000000000f4240611a9487600301548561208b90919063ffffffff16565b81611a9b57fe5b8491900463ffffffff6121d216565b60028601546003870154919250600160801b90046001600160801b031690611b0f907f00000000000000000000000000000000000000000000000000000000000f424090611af990849061208b565b81611b0057fe5b8391900463ffffffff6121d216565b821015611b2e5760405162461bcd60e51b81526004016104c290612600565b7f00000000000000000000000000000000ffffffffffffffffffffffffffffffff6001600160801b0316821115611b775760405162461bcd60e51b81526004016104c290612842565b60007f000000000000000000000000000000000000000000000000000000000000012c63ffffffff16876006015481611bac57fe5b60028901546040516309b0f4e560e21b81529290910492506000916001600160a01b037f00000000000000000000000025b06305cc4ec6afcf3e7c0b673da1ef8ae2631316916326c3d39491611c16918a918c9188918b916001600160801b031690600401612cf8565b602060405180830381600087803b158015611c3057600080fd5b505af1158015611c44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c6891906123fe565b9050808860080181905550887f38f1372ae30d4273e14ea8b395321dc1e4d5fcad60cd0785b0b34ef99b74f2a182604051611ca39190612d35565b60405180910390a2505050505050505050565b336001600160a01b037f000000000000000000000000e716ec63c5673b3a4732d22909b38d779fa47c3f1614611cfe5760405162461bcd60e51b81526004016104c29061264a565b611d29827f000000000000000000000000e716ec63c5673b3a4732d22909b38d779fa47c3f83611ed2565b5050565b7f000000000000000000000000000000000000000000000000000000000000012c81565b7f00000000000000000000000000000000000000000000000000000000000f424081565b611d7d6121f5565b5060009081526002602081815260409283902083516101808101855281546001600160a01b0390811682526001830154811693820193909352928101546001600160801b0380821695850195909552600160801b9004909316606083015260038301546080830152600483015460a08301526005830154811660c0830152600683015460e083015260078301546101008301526008830154610120830152600990920154918216610140820152600160a01b90910460ff16151561016082015290565b604080516000808252602082019092526001600160a01b038416908390604051611e6a9190612436565b60006040518083038185875af1925050503d8060008114611ea7576040519150601f19603f3d011682016040523d82523d6000602084013e611eac565b606091505b5050905080611ecd5760405162461bcd60e51b81526004016104c290612b93565b505050565b60006060846001600160a01b031663a9059cbb8585604051602401611ef892919061252e565b6040516020818303038152906040529060e01b6020820180516001600160e01b038381831617835250505050604051611f319190612436565b6000604051808303816000865af19150503d8060008114611f6e576040519150601f19603f3d011682016040523d82523d6000602084013e611f73565b606091505b5091509150818015611f9d575080511580611f9d575080806020019051810190611f9d9190612350565b611fb95760405162461bcd60e51b81526004016104c2906125c9565b5050505050565b6001600160a01b03811660009081526003602052604081205460ff16611ff85760405162461bcd60e51b81526004016104c2906129a7565b60405163e6a4390560e01b81526001600160a01b0383169063e6a43905906120269087908790600401612483565b60206040518083038186803b15801561203e57600080fd5b505afa158015612052573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612076919061227c565b949350505050565b6001805480820190915590565b60008115806120a6575050808202828282816120a357fe5b04145b6120c25760405162461bcd60e51b81526004016104c290612691565b92915050565b808201828110156120c25760405162461bcd60e51b81526004016104c2906126bf565b60006060846001600160a01b031663095ea7b3858560405160240161211192919061252e565b6040516020818303038152906040529060e01b6020820180516001600160e01b03838183161783525050505060405161214a9190612436565b6000604051808303816000865af19150503d8060008114612187576040519150601f19603f3d011682016040523d82523d6000602084013e61218c565b606091505b50915091508180156121b65750805115806121b65750808060200190518101906121b69190612350565b611fb95760405162461bcd60e51b81526004016104c2906126ed565b808203828111156120c25760405162461bcd60e51b81526004016104c29061259a565b6040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810182905261016081019190915290565b60006020828403121561226a578081fd5b813561227581612d8e565b9392505050565b60006020828403121561228d578081fd5b815161227581612d8e565b600080600080600080600080610100898b0312156122b4578384fd5b88356122bf81612d8e565b975060208901356122cf81612d8e565b965060408901356122df81612db8565b955060608901356122ef81612db8565b94506080890135935060a0890135925060c0890135915060e089013561231481612d8e565b809150509295985092959890939650565b60008060408385031215612337578182fd5b823561234281612d8e565b946020939093013593505050565b600060208284031215612361578081fd5b81518015158114612275578182fd5b600080600060608486031215612384578283fd5b835161238f81612da3565b60208501519093506123a081612da3565b604085015190925063ffffffff811681146123b9578182fd5b809150509250925092565b6000602082840312156123d5578081fd5b815161ffff81168114612275578182fd5b6000602082840312156123f7578081fd5b5035919050565b60006020828403121561240f578081fd5b5051919050565b6001600160a01b03169052565b15159052565b6001600160801b03169052565b60008251815b81811015612456576020818601810151858301520161243c565b818111156124645782828501525b509190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039c8d1681529a8c1660208c01526001600160801b03998a1660408c01529790981660608a0152608089019590955260a088019390935290871660c087015260e086015261010085015261012084015292166101408201529015156101608201526101800190565b6001600160a01b039290921682526001600160801b0316602082015260400190565b6001600160a01b03929092168252602082015260400190565b6020808252825182820181905260009190848201906040850190845b8181101561258357835161ffff1683529284019291840191600101612563565b50909695505050505050565b901515815260200190565b60208082526015908201527464732d6d6174682d7375622d756e646572666c6f7760581b604082015260600190565b6020808252601f908201527f5472616e7366657248656c7065723a205452414e534645525f4641494c454400604082015260600190565b6020808252602a908201527f476e6f73697350726f746f636f6c52656c617965723a20494e56414c49445f50604082015269524943455f52414e474560b01b606082015260800190565b60208082526027908201527f476e6f73697350726f746f636f6c52656c617965723a2043414c4c45525f4e4f6040820152662a2fa7aba722a960c91b606082015260800190565b60208082526014908201527364732d6d6174682d6d756c2d6f766572666c6f7760601b604082015260600190565b60208082526014908201527364732d6d6174682d6164642d6f766572666c6f7760601b604082015260600190565b6020808252601e908201527f5472616e7366657248656c7065723a20415050524f56455f4641494c45440000604082015260600190565b60208082526022908201527f476e6f73697350726f746f636f6c52656c617965723a20554e4b4f574e5f504160408201526124a960f11b606082015260800190565b60208082526029908201527f476e6f73697350726f746f636f6c52656c617965723a204f524445525f4e4f5460408201526817d1561150d555115160ba1b606082015260800190565b6020808252602b908201527f476e6f73697350726f746f636f6c52656c617965723a20444541444c494e455f60408201526a1393d517d4915050d2115160aa1b606082015260800190565b60208082526028908201527f476e6f73697350726f746f636f6c52656c617965723a20494e56414c49445f546040820152674f4c4552414e434560c01b606082015260800190565b6020808252602a908201527f476e6f73697350726f746f636f6c52656c617965723a20414d4f554e545f4f55604082015269545f4f465f52414e474560b01b606082015260800190565b60208082526027908201527f476e6f73697350726f746f636f6c52656c617965723a20444541444c494e455f60408201526614915050d2115160ca1b606082015260800190565b60208082526025908201527f476e6f73697350726f746f636f6c52656c617965723a204f524445525f45584560408201526410d555115160da1b606082015260800190565b60208082526025908201527f476e6f73697350726f746f636f6c52656c617965723a20524553455256455f546040820152644f5f4c4f5760d81b606082015260800190565b6020808252602a908201527f476e6f73697350726f746f636f6c52656c617965723a204f42534552564154496040820152694f4e5f52554e4e494e4760b01b606082015260800190565b60208082526026908201527f476e6f73697350726f746f636f6c52656c617965723a20494e56414c49445f466040820152654143544f525960d01b606082015260800190565b60208082526028908201527f476e6f73697350726f746f636f6c52656c617965723a204f425345525641544960408201526713d397d15391115160c21b606082015260800190565b60208082526027908201527f476e6f73697350726f746f636f6c52656c617965723a20494e56414c49445f446040820152664541444c494e4560c81b606082015260800190565b6020808252602b908201527f476e6f73697350726f746f636f6c52656c617965723a20494e56414c49445f5460408201526a13d2d15397d05353d5539560aa1b606082015260800190565b60208082526025908201527f476e6f73697350726f746f636f6c52656c617965723a20494e5355464649454e6040820152640a8be8aa8960db1b606082015260800190565b60208082526023908201527f476e6f73697350726f746f636f6c52656c617965723a20494e56414c49445f5060408201526220a4a960e91b606082015260800190565b60208082526024908201527f476e6f73697350726f746f636f6c52656c617965723a20494e56414c49445f4f604082015263292222a960e11b606082015260800190565b60208082526023908201527f5472616e7366657248656c7065723a204554485f5452414e534645525f46414960408201526213115160ea1b606082015260800190565b6020808252602a908201527f476e6f73697350726f746f636f6c52656c617965723a20494e5355464649454e6040820152692a2faa27a5a2a72fa4a760b11b606082015260800190565b600061018082019050612c34828451612416565b6020830151612c466020840182612416565b506040830151612c596040840182612429565b506060830151612c6c6060840182612429565b506080830151608083015260a083015160a083015260c0830151612c9360c0840182612416565b5060e083810151908301526101008084015190830152610120808401519083015261014080840151612cc782850182612416565b505061016080840151612cdc82850182612423565b505092915050565b6001600160801b0391909116815260200190565b61ffff958616815293909416602084015263ffffffff9190911660408301526001600160801b039081166060830152909116608082015260a00190565b90815260200190565b9182526001600160a01b0316602082015260400190565b9283526001600160a01b039190911660208301526001600160801b0316604082015260600190565b63ffffffff91909116815260200190565b6001600160a01b03811681146110f457600080fd5b6001600160701b03811681146110f457600080fd5b6001600160801b03811681146110f457600080fdfea26469706673582212203da53e99b923809ac33371e120e9afc1422099da2e25198361b809b5b41874a664736f6c63430006060033