Feature Tip: Add private address tag to any address under My Name Tag !
Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
UniversalRouter
Compiler Version
v0.8.30+commit.73712a01
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
/**
* @title DSF UniversalRouter
* @author Andrei Averin — CTO dsf.finance
* @notice A universal router that combines DEX modules (Curve, UniswapV2, SushiSwap, UniswapV3, etc.)
* and performs single and split swaps with automatic commission withholding.
* @dev Router:
* - Requests the best quotes (1-hop and 2-hop) from all modules;
* - Selects the optimal route or split between the two best;
* - Pulls tokens from the user, approves modules, and executes the swap;
* - Charges a fee from the swap (feeBpsSwap) and from positive slippage (feeBpsPositive);
* - Supports ETH↔WETH, secure calls, and module list management.
*
* Uses low-level staticcall to IDexModule.getBestRoute(address,address,uint256)
* and a unified payload format:
* abi.encode(module,index,quotedOut,tokenIn,tokenOut,amountIn,bytes[] route).
*/
/* ─────────────────────────────── Interfaces / Types ─────────────────────────────── */
struct DexRoute { bytes[] data; }
struct Quote {
address pool;
int128 i;
int128 j;
bool useUnderlying;
uint256 amountOut;
}
struct BestAgg {
bytes payload;
uint256 amount;
address module;
uint256 idx;
}
struct RouteInfo {
address module;
uint256 index;
bytes payload; // the same format as in getBestRoute/decodeRoute
uint256 amount; // quotedOut
}
struct QuoteArgs {
address tokenIn;
address tokenOut;
uint256 amountIn;
}
struct LegDecoded {
address module;
uint256 index;
uint256 quoted; // quotedOut from payload
address tokenIn;
address tokenOut;
uint256 amountIn;
bytes[] route;
}
struct SplitResult {
address moduleA;
address moduleB;
address tokenIn;
address tokenOut;
uint256 totalIn;
uint256 amountInA;
uint256 amountInB;
uint256 outA;
uint256 outB;
uint256 totalOut;
}
struct TrackedRoute {
bytes payload;
uint256 amountOut;
address module;
uint256 moduleIndex;
}
struct BestQuotes {
TrackedRoute top1Hop;
TrackedRoute second1Hop;
TrackedRoute top2Hop;
TrackedRoute second2Hop;
}
struct ModuleQuotes {
address module;
uint256 moduleIndex;
bytes payload1Hop;
uint256 amountOut1Hop;
bytes payload2Hop;
uint256 amountOut2Hop;
}
interface IFeedRegistry {
function latestRoundData(address base, address quote)
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function decimals(address base, address quote) external view returns (uint8);
}
interface IDexModule {
/**
* @notice Compute the best 1-hop and 2-hop routes.
* @param tokenIn Input token
* @param tokenOut Output token
* @param amountIn Input amount
* @return best1HopRoute Serialized 1-hop route
* @return amountOut1Hop Quoted 1-hop output
* @return best2HopRoute Serialized 2-hop route
* @return amountOut2Hop Quoted 2-hop output
*/
function getBestRoute(
address tokenIn,
address tokenOut,
uint256 amountIn
) external view returns (
DexRoute memory best1HopRoute,
uint256 amountOut1Hop,
DexRoute memory best2HopRoute,
uint256 amountOut2Hop
);
/**
* @notice Execute a previously returned route with a slippage check based on a percentage.
* @param route Serialized route
* @param to Recipient of the final tokens
* @param percent Percentage (0-100) of amountIn from the route to be swapped. 100 = 100%.
* @return amountOut Actual output received
*/
function swapRoute(
DexRoute calldata route,
address to,
uint256 percent
) external returns (uint256 amountOut);
/**
* @notice Simulate a route (1–2 hops) encoded as {DexRoute}.
* @param route Serialized route
* @param percent Percentage (0-100)
* @return amountOut Quoted total output amount
*/
function simulateRoute(
DexRoute calldata route,
uint256 percent
) external view returns (uint256 amountOut);
}
interface IWETH {
function deposit() external payable;
function withdraw(uint256 amount) external;
function transfer(address to, uint256 amount) external returns (bool);
function balanceOf(address) external view returns (uint256);
}
contract UniversalRouter is Ownable, ReentrancyGuard {
using SafeERC20 for IERC20;
/* ─────────────────────────────── Storage ─────────────────────────────── */
address constant DENOM_ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
address constant DENOM_USD = 0x0000000000000000000000000000000000000348;
// Chainlink Feed Registry (mainnet)
address public feedRegistry = 0x47Fb2585D2C56Fe188D0E6ec628a38b74fCeeeDf;
address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address[] public modules; // list of modules (Curve, UniV2, Sushi, UniV3)
mapping(address => bool) public isModule;
mapping(address => uint256) private moduleIndexPlusOne; // 1-based for O(1) remove
/* ───────────────────────────── Fees config ───────────────────────────── */
address public feeRecipient; // commission recipient address
uint16 public feeBpsSwap; // commission in bps (max 10000 = 100%)
uint16 public feeBpsPositive; // commission with positive slippage, bps (max 10000 = 100%)
/* ────────────────────────────── Fees caps ────────────────────────────── */
uint16 public constant MAX_FEE_SWAP_BPS = 100; // 1%
uint16 public constant MAX_FEE_POSITIVE_BPS = 10_000; // 100%
/* ─────────────────────────────── Events ──────────────────────────────── */
event ModuleAdded(address indexed module);
event ModuleRemoved(address indexed module);
event ModulesReset(uint256 newCount);
event FeeConfigUpdated(address indexed recipient, uint16 bpsSwap, uint16 bpsPositive);
event ERC20Recovered(address indexed token, address indexed to, uint256 amount);
event ETHSwept(address indexed to, uint256 amount);
/**
* @notice Execution of a single swap.
* @param module Module that executed the route.
* @param user Initiator (msg.sender).
* @param to Recipient of the final funds.
* @param tokenIn Input token.
* @param tokenOut Output token.
* @param amountIn Input amount (withdrawn from the user).
* @param amountOut Final amount after fees (net).
* @param quotedOut Expected output (quota from payload).
*/
event SwapExecuted(
address indexed module,
address indexed user,
address indexed to,
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOut,
uint256 quotedOut
);
/**
* @notice Execution of a split swap via two routes.
* @param user Initiator (msg.sender).
* @param to Recipient of the final funds.
* @param moduleA Module A.
* @param moduleB Module B.
* @param tokenIn Input token (or WETH for ETH route).
* @param tokenOut Output token.
* @param totalIn Total input.
* @param totalOut Total output (after fees — if the event is emitted after distribution).
* @param bpsA Share A in percent (0–100).
*/
event SwapSplitExecuted(
address indexed user,
address indexed to,
address moduleA,
address moduleB,
address tokenIn,
address tokenOut,
uint256 totalIn,
uint256 totalOut,
uint16 bpsA
);
/* ─────────────────────────────── Errors ─────────────────────────────── */
error ZeroAddress();
error DuplicateModule();
error NotAModule();
error NoRouteFound();
/* ─────────────────────────────── Modifiers ─────────────────────────────── */
modifier onlyERC20(address token) {
require(token != address(0) && token.code.length > 0, "not ERC20");
_;
}
/* ──────────────────────────────── receive ───────────────────────────────── */
/// @notice Needed to get native ETH (e.g., with IWETH.withdraw()).
receive() external payable {}
/* ─────────────────────────────── Constructor ─────────────────────────────── */
/**
* @notice Deploys the router and configures modules and commission parameters.
* @param _modules List of module addresses (Curve/UniV2/UniV3/…).
* @param _feeRecipient Address of the commission recipient.
* @param _feeBpsSwap Swap fee, bps (max. limited by require inside).
* @param _feeBpsPositive Positive slippage fee, bps (max. limited by require inside).
*/
constructor(
address[] memory _modules,
address _feeRecipient,
uint16 _feeBpsSwap,
uint16 _feeBpsPositive
) Ownable(msg.sender) {
_setModules(_modules);
require(_feeBpsSwap <= MAX_FEE_SWAP_BPS, "UR: swap fee too high");
require(_feeBpsPositive <= MAX_FEE_POSITIVE_BPS, "UR: pos fee too high");
feeRecipient = _feeRecipient;
feeBpsSwap = _feeBpsSwap;
feeBpsPositive = _feeBpsPositive;
emit FeeConfigUpdated(feeRecipient, feeBpsSwap, feeBpsPositive);
}
/* ────────────────────────────── Admin: Registry ──────────────────────────── */
function setFeedRegistry(address reg) external onlyOwner {
require(reg != address(0), "UR: bad registry");
require(reg.code.length > 0, "UR: registry not a contract");
feedRegistry = reg;
}
/* ──────────────────────────── Admin: modules mgmt ────────────────────────── */
/**
* @notice Complete reset of the module list.
* @dev Clears old ones, adds new ones, emits ModulesReset.
* @param _modules New list of modules.
*/
function setModules(address[] calldata _modules) external onlyOwner {
_clearModules();
_addModules(_modules);
emit ModulesReset(_modules.length);
}
/**
* @notice Add module to allowlist.
* @param module Address of IDexModule module.
*/
function addModule(address module) external onlyOwner {
_addModule(module);
}
/**
* @notice Remove module from allowlist.
* @param module Address of IDexModule module.
*/
function removeModule(address module) external onlyOwner {
_removeModule(module);
}
/**
* @notice Returns the number of connected modules.
* @return The length of the modules array.
*/
function modulesLength() external view returns (uint256) {
return modules.length;
}
/**
* @notice Returns the current list of modules.
* @dev The array is returned in memory (a copy of the state).
* @return An array of module addresses.
*/
function getModules() external view returns (address[] memory) {
return modules;
}
/* ────────────────────────────────── Admin ────────────────────────────────── */
/**
* @notice Rescue all stuck ERC-20 tokens to `to` or owner.
* @dev Owner-only. Uses SafeERC20. Always sends the entire token balance
* held by this contract. If `to` is zero, defaults to owner().
* @param token ERC-20 token address to rescue (must be non-zero).
* @param to Recipient; if zero address, defaults to owner().
*/
function recoverERC20(address token, address to)
external
onlyOwner
nonReentrant
{
require(token != address(0), "UR: token=0");
address recipient = (to == address(0)) ? owner() : to;
uint256 amt = IERC20(token).balanceOf(address(this));
if (amt == 0) return; // nothing to do
IERC20(token).safeTransfer(recipient, amt);
emit ERC20Recovered(token, recipient, amt);
}
/**
* @notice Transfers all remaining ETH from the contract to the owner or specified address.
* @dev Only for the owner (onlyOwner). Added nonReentrant to protect against repeated calls.
* If the `to` parameter is equal to a zero address, the funds are sent to the contract owner.
* @param to The address of the ETH recipient (if 0x0 — send to the owner).
*/
function sweepETH(address to)
external
onlyOwner
nonReentrant
{
address recipient = (to == address(0)) ? owner() : to;
uint256 bal = address(this).balance;
(bool ok,) = recipient.call{value: bal}("");
require(ok, "ETH sweep failed");
emit ETHSwept(recipient, bal);
}
/* ───────────────────────────────── Admin: fee ────────────────────────────── */
/**
* @notice Update the address of the commission recipient.
* @param _recipient New address of the fee recipient.
*/
function setFeeRecipient(address _recipient) external onlyOwner {
feeRecipient = _recipient;
emit FeeConfigUpdated(feeRecipient, feeBpsSwap, feeBpsPositive);
}
/**
* @notice Update commission percentages.
* @dev Upper limit checked via require; emits FeeConfigUpdated.
* @param _feeBpsSwap Swap commission, bps.
* @param _feeBpsPositive Positive slippage commission, bps.
*/
function setFeePercents(uint16 _feeBpsSwap, uint16 _feeBpsPositive) external onlyOwner {
require(_feeBpsSwap <= MAX_FEE_SWAP_BPS, "UR: swap fee too high");
require(_feeBpsPositive <= MAX_FEE_POSITIVE_BPS, "UR: pos fee too high");
feeBpsSwap = _feeBpsSwap;
feeBpsPositive = _feeBpsPositive;
emit FeeConfigUpdated(feeRecipient, feeBpsSwap, feeBpsPositive);
}
/**
* @notice Completely reinstalls the list of modules.
* @dev Clears the current modules, then adds new ones. Emits ModulesReset.
* @param _modules New list of modules.
*/
function _setModules(address[] memory _modules) internal {
_clearModules();
uint256 n = _modules.length;
for (uint256 i; i < n; ) {
_addModule(_modules[i]);
unchecked { ++i; }
}
emit ModulesReset(n);
}
/**
* @notice Resets (clears) all modules.
* @dev Resets isModule and indexes; clears the modules array.
*/
function _clearModules() internal {
uint256 n = modules.length;
for (uint256 i; i < n; ) {
address m = modules[i];
isModule[m] = false;
moduleIndexPlusOne[m] = 0;
unchecked { ++i; }
}
delete modules;
}
/**
* @notice Adds modules in bulk.
* @dev Calls _addModule for each address.
* @param _modules List of module addresses.
*/
function _addModules(address[] calldata _modules) internal {
uint256 n = _modules.length;
for (uint256 i; i < n; ) {
_addModule(_modules[i]);
unchecked { ++i; }
}
}
/**
* @notice Adds one module to the allowlist.
* @dev Checks for a non-zero address, the presence of code, and the absence of duplicates.
* Updates isModule, modules, and moduleIndexPlusOne. Emits ModuleAdded.
* @param module The module contract address.
*/
function _addModule(address module) internal {
if (module == address(0)) revert ZeroAddress();
if (isModule[module]) revert DuplicateModule();
// (опционально) минимальная проверка кода
if (module.code.length == 0) revert ZeroAddress();
isModule[module] = true;
modules.push(module);
moduleIndexPlusOne[module] = modules.length; // 1-based
emit ModuleAdded(module);
}
/**
* @notice Removes a module from the allowlist.
* @dev Performs O(1) removal via swap-pop, supporting 1-based indexing.
* Emit ModuleRemoved.
* @param module Address of the module to be removed.
*/
function _removeModule(address module) internal {
uint256 idxPlusOne = moduleIndexPlusOne[module];
if (idxPlusOne == 0) revert NotAModule();
uint256 idx = idxPlusOne - 1;
uint256 lastIdx = modules.length - 1;
if (idx != lastIdx) {
address last = modules[lastIdx];
modules[idx] = last;
moduleIndexPlusOne[last] = idx + 1;
}
modules.pop();
isModule[module] = false;
moduleIndexPlusOne[module] = 0;
emit ModuleRemoved(module);
}
/* ─────────────────────────────── WETH Helpers ────────────────────────────── */
/**
* @dev Wraps incoming native ETH into WETH.
* @param amount Amount of ETH to wrap (msg.value).
*/
function _wrapETH(uint256 amount) internal {
IWETH(WETH).deposit{value: amount}();
}
/**
* @dev Converts WETH back to ETH and sends it to the recipient.
* @param amount Amount of WETH to convert.
* @param to Recipient's native ETH address.
*/
function _unwrapWETHAndSend(uint256 amount, address to) internal {
require(IWETH(WETH).balanceOf(address(this)) >= amount, "UR: insufficient WETH");
IWETH(WETH).withdraw(amount);
// Send native ETH
(bool success,) = to.call{value: amount}("");
require(success, "UR: ETH transfer failed");
}
/* ───────────────────────────── ETH payout/guards ─────────────────────────── */
/**
* @notice Ensures that tokenIn == WETH in the input payload.
* @dev Reads the address from slot 3 of the payload ABI header (see _loadAddressFromPayload).
* @param payload ABI-encoded route: (module,index,quotedOut,tokenIn,tokenOut,amountIn,bytes[]).
*/
function _requireWethIn(bytes calldata payload) internal pure {
address tokenIn = _loadAddressFromPayload(payload, 3);
require(tokenIn == WETH, "UR: payload tokenIn != WETH");
}
/**
* @notice Ensures that tokenOut == WETH in the output payload.
* @dev Reads the address from the ABI header payload in slot 4 (see _loadAddressFromPayload).
* @param payload ABI-encoded route: (module,index,quotedOut,tokenIn,tokenOut,amountIn,bytes[]).
*/
function _requireWethOut(bytes calldata payload) internal pure {
address tokenOut = _loadAddressFromPayload(payload, 4);
require(tokenOut == WETH, "UR: payload tokenOut != WETH");
}
/**
* @notice Quick reading of the address from the ABI header payload.
* @dev The slot corresponds to the position of a 32-byte word in abi.encode(...).
* 0: module, 1: index, 2: quotedOut, 3: tokenIn, 4: tokenOut, 5: amountIn, 6: offset(bytes[]).
* @param payload Full ABI payload.
* @param slot Slot number (0-based).
* @return a Address read from the specified slot.
*/
function _loadAddressFromPayload(bytes calldata payload, uint256 slot) internal pure returns (address a) {
assembly ("memory-safe") {
a := shr(96, calldataload(add(payload.offset, mul(slot, 32))))
}
}
/* ────────────────────────────────── Helpers ──────────────────────────────── */
/**
* @notice Updates the best quotes in 1-hop and 2-hop segments.
* @dev Supports “top-1” and “top-2” for each category.
* @param currentBest Current best routes.
* @param newRoute Candidate for inclusion.
* @param is1Hop 1-hop (true) or 2-hop (false) flag.
*/
function _updateBestQuotes(BestQuotes memory currentBest, TrackedRoute memory newRoute, bool is1Hop) private pure {
if (is1Hop) {
if (newRoute.amountOut > currentBest.top1Hop.amountOut) {
currentBest.second1Hop = currentBest.top1Hop;
currentBest.top1Hop = newRoute;
} else if (newRoute.amountOut > currentBest.second1Hop.amountOut) {
currentBest.second1Hop = newRoute;
}
} else { // 2-hop
if (newRoute.amountOut > currentBest.top2Hop.amountOut) {
currentBest.second2Hop = currentBest.top2Hop;
currentBest.top2Hop = newRoute;
} else if (newRoute.amountOut > currentBest.second2Hop.amountOut) {
currentBest.second2Hop = newRoute;
}
}
}
/**
* @notice Updates the two absolute best routes found so far (overall Top-1 and Top-2).
* @dev If the new route beats Top-1, it becomes Top-1 and the old Top-1 shifts to Top-2.
* Otherwise, if it only beats Top-2, it replaces Top-2.
* @param top1 Current absolute best route (Top-1).
* @param top2 Current second absolute best route (Top-2).
* @param newRoute Newly observed candidate route to compare against the tops.
* @return Updated Top-1 and Top-2 routes (in this order).
*/
function _updateTopOverall(
TrackedRoute memory top1,
TrackedRoute memory top2,
TrackedRoute memory newRoute
) private pure returns (TrackedRoute memory, TrackedRoute memory) {
if (newRoute.amountOut > top1.amountOut) {
top2 = top1;
top1 = newRoute;
} else if (newRoute.amountOut > top2.amountOut) {
top2 = newRoute;
}
return (top1, top2);
}
/**
* @notice Queries a module for the best 1-hop and 2-hop quotes and packages them as payloads.
* @dev Calls IDexModule.getBestRoute via staticcall and, if non-zero quotes are returned,
* encodes payloads as abi.encode(module, index, quotedOut, tokenIn, tokenOut, amountIn, route.data).
* If the module is not registered or the call fails/returns empty, the struct remains zeroed.
* @param m Module address being queried.
* @param idx Module index (stored for payload bookkeeping).
* @param a Quote arguments (tokenIn, tokenOut, amountIn).
* @return quotes Struct holding module info, 1-hop/2-hop amounts and payloads (if any).
*/
function _getModuleQuotes(
address m,
uint256 idx,
QuoteArgs memory a
) internal view returns (ModuleQuotes memory quotes) {
quotes.module = m;
quotes.moduleIndex = idx;
if (!isModule[m]) return quotes;
bytes memory cd = abi.encodeWithSelector(
IDexModule.getBestRoute.selector,
a.tokenIn,
a.tokenOut,
a.amountIn
);
(bool success, bytes memory ret) = m.staticcall(cd);
if (!success || ret.length == 0) return quotes;
(
DexRoute memory route1, uint256 out1,
DexRoute memory route2, uint256 out2
) = abi.decode(ret, (DexRoute, uint256, DexRoute, uint256));
// Build payloads only for non-zero, non-empty routes.
if (out1 > 0 && route1.data.length > 0) {
quotes.amountOut1Hop = out1;
quotes.payload1Hop = abi.encode(
m, idx, out1, a.tokenIn, a.tokenOut, a.amountIn, route1.data
);
}
if (out2 > 0 && route2.data.length > 0) {
quotes.amountOut2Hop = out2;
quotes.payload2Hop = abi.encode(
m, idx, out2, a.tokenIn, a.tokenOut, a.amountIn, route2.data
);
}
}
/**
* @dev Private helper function for calculating the total output amount.
* @param percentA Percentage of amountIn for Route A (0-100).
*/
function _calculateTotalOut(
address moduleA,
bytes[] memory routeA,
address moduleB,
bytes[] memory routeB,
uint16 percentA // 0-100
) internal view returns (uint256 totalOut) {
uint16 percentB = 100 - percentA;
// simulateRoute for A (percent 0–100)
uint256 outA = IDexModule(moduleA).simulateRoute(DexRoute({ data: routeA }), percentA);
// simulateRoute for B (percent 0–100)
uint256 outB = IDexModule(moduleB).simulateRoute(DexRoute({ data: routeB }), percentB);
return outA + outB;
}
/**
* @notice Safely sets the allowance to the required minimum.
* @dev If the current allowance < amount, first set it to zero (if >0), then set it to type(uint256).max.
* Uses SafeERC20.forceApprove for maximum compatibility.
* @param token ERC20 token address.
* @param spender Contract address to which we issue the allowance.
* @param amount Minimum required limit.
*/
function _smartApprove(address token, address spender, uint256 amount) internal {
uint256 cur = IERC20(token).allowance(address(this), spender);
if (cur < amount) {
if (cur > 0) IERC20(token).forceApprove(spender, 0);
IERC20(token).forceApprove(spender, type(uint256).max);
}
}
/**
* @notice Emits the consolidated split-swap execution event.
* @dev Packs the essential split data into a single event for off-chain indexing/analytics.
* @param r Split result struct (modules, tokens, totals).
* @param user Original caller (initiator).
* @param to Final receiver of the swapped tokens/ETH.
* @param bpsA Portion routed through module A, in percent (0–100).
*/
function _emitSwapSplit(
SplitResult memory r,
address user,
address to,
uint16 bpsA
) internal {
emit SwapSplitExecuted(
user,
to,
r.moduleA,
r.moduleB,
r.tokenIn,
r.tokenOut,
r.totalIn,
r.totalOut,
bpsA
);
}
/**
* @notice Decodes a route payload (in memory) into a typed struct used by the router.
* @dev Expects payload encoded as:
* (address module, uint256 index, uint256 quoted, address tokenIn, address tokenOut, uint256 amountIn, bytes[] route)
* @param payload ABI-encoded payload stored in memory.
* @return d Decoded LegDecoded struct.
*/
function _decodeRouteStruct(bytes memory payload)
internal
pure
returns (LegDecoded memory d)
{
(d.module, d.index, d.quoted, d.tokenIn, d.tokenOut, d.amountIn, d.route) =
abi.decode(payload, (address, uint256, uint256, address, address, uint256, bytes[]));
}
/**
* @notice Decodes a route payload (in calldata) into a typed struct used by the router.
* @dev Same layout as the memory version, but reads directly from calldata to save gas.
* @param payload ABI-encoded payload residing in calldata.
* @return d Decoded LegDecoded struct.
*/
function _decodeRouteStructCallData(bytes calldata payload)
internal
pure
returns (LegDecoded memory d)
{
(d.module, d.index, d.quoted, d.tokenIn, d.tokenOut, d.amountIn, d.route) =
abi.decode(payload, (address, uint256, uint256, address, address, uint256, bytes[]));
}
/**
* @notice Distribution of commissions and ERC20 transfer.
* @dev Retains fix-fee (feeBpsSwap) and % of positive slippage (feeBpsPositive).
* @param token ERC20 address.
* @param to Recipient.
* @param grossOut Actual output after swap(s).
* @param quotedOut Quote (expectation).
* @param minOut Minimum acceptable output.
* @return netOut Amount after commissions.
*/
function _distributeTokenWithFees(
address token,
address to,
uint256 grossOut, // actual output after swap(s)
uint256 quotedOut, // quoted (expected) output
uint256 minOut
) internal returns (uint256 netOut) {
if (grossOut == 0) return 0;
uint256 baseline = quotedOut > minOut ? quotedOut : minOut;
uint256 feeSwap = 0;
uint256 feePos = 0;
// take fees only if recipient is set and bps > 0
if (feeRecipient != address(0)) {
if (feeBpsSwap > 0) {
unchecked { feeSwap = (grossOut * feeBpsSwap) / 10_000; }
}
if (feeBpsPositive > 0 && grossOut > baseline) {
unchecked { feePos = ((grossOut - baseline) * feeBpsPositive) / 10_000; }
}
}
uint256 totalFee = feeSwap + feePos;
// safety guard against overflow/rounding:
if (totalFee > grossOut) totalFee = grossOut;
netOut = grossOut - totalFee;
// Payouts: send fee to feeRecipient first, then net to user
if (totalFee > 0) {
IERC20(token).safeTransfer(feeRecipient, totalFee);
}
IERC20(token).safeTransfer(to, netOut);
}
/**
* @notice Distribution of fees and transfer of ETH.
* @dev Similar to _distributeTokenWithFees, but for ETH.
* @param to Recipient.
* @param grossEth Actual ETH output.
* @param quotedOutEth Expected output.
* @param minOutEth Minimum allowable output.
* @return netOut Amount after fees.
*/
function _distributeETHWithFees(
address to,
uint256 grossEth, // actual ETH output
uint256 quotedOutEth, // expected output (WETH==ETH)
uint256 minOutEth
) internal returns (uint256 netOut) {
if (grossEth == 0) return 0;
uint256 baseline = quotedOutEth > minOutEth ? quotedOutEth : minOutEth;
uint256 feeSwap = 0;
uint256 feePos = 0;
if (feeRecipient != address(0)) {
if (feeBpsSwap > 0) {
unchecked { feeSwap = (grossEth * feeBpsSwap) / 10_000; }
}
if (feeBpsPositive > 0 && grossEth > baseline) {
unchecked { feePos = ((grossEth - baseline) * feeBpsPositive) / 10_000; }
}
}
uint256 totalFee = feeSwap + feePos;
if (totalFee > grossEth) totalFee = grossEth;
netOut = grossEth - totalFee;
if (totalFee > 0) {
(bool fs, ) = feeRecipient.call{value: totalFee}("");
require(fs, "fee ETH xfer failed");
}
(bool ok, ) = to.call{value: netOut}("");
require(ok, "ETH xfer failed");
}
/**
* @notice Safely reads balance, allowance, and decimals for a (token, wallet, spender).
* @dev For ETH (token==address(0)): returns (wallet.balance, 0, 18).
* Uses low-level staticcall to tolerate non-standard ERC-20s (e.g., USDT).
* If {decimals()} cannot be read, returns 0 as a sentinel value.
* @param token ERC-20 token address (or address(0) for ETH).
* @param wallet Address whose balance is queried.
* @param spender Address whose allowance is checked.
* @return bal Token balance of {wallet} (or ETH balance if token==address(0)).
* @return allow_ Current allowance from {wallet} to {spender} (0 for ETH).
* @return decs Token decimals (18 for ETH; 0 if unreadable).
*/
function _safeBalanceAndAllowance(
address token,
address wallet,
address spender
) internal view returns (uint256 bal, uint256 allow_, uint8 decs) {
if (token == address(0)) {
// ETH: allowance not applicable
return (wallet.balance, 0, 18);
}
// balanceOf(wallet)
(bool ok1, bytes memory data1) =
token.staticcall(abi.encodeWithSelector(IERC20.balanceOf.selector, wallet));
if (ok1 && data1.length >= 32) {
bal = abi.decode(data1, (uint256));
} else {
bal = 0;
}
// allowance(wallet, spender)
(bool ok2, bytes memory data2) =
token.staticcall(abi.encodeWithSelector(IERC20.allowance.selector, wallet, spender));
if (ok2 && data2.length >= 32) {
allow_ = abi.decode(data2, (uint256));
} else {
allow_ = 0;
}
// decimals() selector = 0x313ce567
(bool ok3, bytes memory data3) =
token.staticcall(abi.encodeWithSelector(bytes4(0x313ce567)));
if (ok3 && data3.length >= 32) {
// Some tokens return uint256, some return uint8. Read as uint256 and cast safely.
uint256 d = abi.decode(data3, (uint256));
decs = d > 255 ? uint8(255) : uint8(d);
} else {
// if reading failed — 0, so that the caller understands that it is undefined
decs = 0;
}
}
/**
* @notice Reads the base/quote price from the Chainlink Feed Registry and normalizes it to 1e18.
* @dev Never reverts. Returns (price1e18=0, updatedAt=0) if the feed is missing or invalid.
* No freshness check is performed here; callers may validate staleness separately.
* @param base Address of the base asset (token address or Chainlink denomination, e.g. DENOM_ETH).
* @param quote Address of the quote asset (typically DENOM_USD).
* @return price1e18 Base/quote price scaled to 1e18 (0 if unavailable).
* @return updatedAt Timestamp of the last feed update (0 if unavailable).
*/
function _pairPrice1e18(address base, address quote)
private
view
returns (uint256 price1e18, uint256 updatedAt)
{
address reg = feedRegistry;
if (reg == address(0)) return (0, 0);
try IFeedRegistry(reg).latestRoundData(base, quote) returns (
uint80, int256 answer, uint256, uint256 upd, uint80
) {
if (answer <= 0) return (0, upd);
uint8 dec = IFeedRegistry(reg).decimals(base, quote);
uint256 u = uint256(answer);
if (dec < 18) price1e18 = u * 10 ** (18 - dec);
else if (dec > 18) price1e18 = u / 10 ** (dec - 18);
else price1e18 = u;
return (price1e18, upd);
} catch {
return (0, 0);
}
}
/**
* @notice Returns the price of 1 token in USD (1e18) via Chainlink Feed Registry.
* @dev Never reverts. **Does not** check data freshness; returns whatever the registry holds.
* Resolution order:
* - If `asEth == true` or `token == address(0)`: use ETH/USD;
* - Else try direct TOKEN/USD;
* - If `token == WETH`: use ETH/USD;
* - Else try TOKEN/ETH × ETH/USD.
* @param token Token address (or address(0) for ETH).
* @param asEth If true, force using the ETH/USD price (e.g., for WETH, stETH, etc.).
* @return price1e18 The price of 1 token in USD (1e18), or 0 if unavailable.
* @return updatedAt The timestamp of the last feed update used for the computation.
*/
function _tryTokenUsdPrice1e18(address token, bool asEth)
internal
view
returns (uint256 price1e18, uint256 updatedAt)
{
// ETH or forcibly as ETH
if (asEth || token == address(0)) {
(price1e18, updatedAt) = _pairPrice1e18(DENOM_ETH, DENOM_USD);
if (price1e18 != 0) return (price1e18, updatedAt);
}
// Direct TOKEN/USD
(price1e18, updatedAt) = _pairPrice1e18(token, DENOM_USD);
if (price1e18 != 0) return (price1e18, updatedAt);
// WETH → ETH/USD
if (token == WETH) {
(price1e18, updatedAt) = _pairPrice1e18(DENOM_ETH, DENOM_USD);
return (price1e18, updatedAt);
}
// Attempt via TOKEN/ETH × ETH/USD (relevant for LST, etc.)
(uint256 tEth, uint256 updA) = _pairPrice1e18(token, DENOM_ETH);
if (tEth == 0) return (0, updA);
(uint256 ethUsd, uint256 updB) = _pairPrice1e18(DENOM_ETH, DENOM_USD);
if (ethUsd == 0) return (0, updB);
uint256 minUpd = updA < updB ? updA : updB;
return ((tEth * ethUsd) / 1e18, minUpd);
}
/* ──────────────────────────────────── Read ───────────────────────────────── */
/**
* @notice Reads token→USD price (1e18) via Chainlink Feed Registry without checking “freshness”.
* @dev Returns 0 if the feed is missing or `answer <= 0`. ETH is passed as address(0).
* @param token Token address (or address(0) for ETH).
* @param asEth If true, use ETH/USD price (for WETH, stETH, etc.).
* @return usdPerToken1e18 Token price in USD (1e18) or 0.
* @return updatedAt Time of the last feed update.
*/
function tryTokenUsdPrice1e18(address token, bool asEth)
external
view
returns (uint256 usdPerToken1e18, uint256 updatedAt)
{
(usdPerToken1e18, updatedAt) = _tryTokenUsdPrice1e18(token, asEth);
return (usdPerToken1e18, updatedAt);
}
/**
* @notice Returns balance, allowance, and decimals for a (token, wallet, spender).
* @dev For ETH (token==address(0)): returns (wallet.balance, 0, 18).
* Decimals is 0 if the token's {decimals()} cannot be read.
* @param token ERC-20 token address (or address(0) for ETH).
* @param wallet Address whose balance is queried.
* @param spender Address whose allowance is checked.
* @return balance Token balance of {wallet} (or ETH balance if token==address(0)).
* @return allowance_ Current allowance from {wallet} to {spender} (0 for ETH).
* @return decimals_ Token decimals (18 for ETH; 0 if unreadable).
*/
function balanceAndAllowanceOf(
address token,
address wallet,
address spender
) external view returns (uint256 balance, uint256 allowance_, uint8 decimals_) {
(balance, allowance_, decimals_) = _safeBalanceAndAllowance(token, wallet, spender);
}
/**
* @notice Returns balance, allowance, decimals, and USD price for a single token.
* @dev For ETH (token==address(0)): returns (wallet.balance, 0, 18) and uses ETH/USD price when {asEth} is true.
* Decimals is 0 if the token's {decimals()} cannot be read.
* @param token ERC-20 token address (or address(0) for ETH).
* @param wallet Address whose balance is queried.
* @param spender Address whose allowance is checked.
* @param asEth If true, forces the ETH/USD feed for this token.
* @return balance Token balance of {wallet} (or ETH balance if token==address(0)).
* @return allowance_ Current allowance from {wallet} to {spender} (0 for ETH).
* @return usdPerToken1e18 Price of 1 token in USD (scaled to 1e18), 0 if no feed/<=0.
* @return updatedAt Timestamp of the last price update.
* @return decimals_ Token decimals (18 for ETH; 0 if unreadable).
*/
function balanceAllowanceAndUsd(
address token,
address wallet,
address spender,
bool asEth
)
external
view
returns (
uint256 balance,
uint256 allowance_,
uint256 usdPerToken1e18,
uint256 updatedAt,
uint8 decimals_
)
{
// 1) safely read balance/allowance (do not revert to non-standard ERC-20)
(balance, allowance_, decimals_) = _safeBalanceAndAllowance(token, wallet, spender);
// 2) Let's try to get the price in USD (gently, without revert)
(usdPerToken1e18, updatedAt) = _tryTokenUsdPrice1e18(token, asEth);
}
/**
* @notice Returns balance, allowance, decimals, and USD price for two tokens at once.
* @dev For ETH (token==address(0)): returns (wallet.balance, 0, 18) and uses ETH/USD price if requested via {asEthIn}/{asEthOut}.
* Decimals is 0 if a token's {decimals()} cannot be read.
* @param wallet Address whose balances are queried.
* @param spender Address whose allowances are checked.
* @param tokenIn First token address (or address(0) for ETH).
* @param asEthIn If true, forces ETH/USD feed for tokenIn.
* @param tokenOut Second token address (or address(0) for ETH).
* @param asEthOut If true, forces ETH/USD feed for tokenOut.
* @return balanceIn {wallet} balance of tokenIn (ETH if tokenIn==address(0)).
* @return allowance_In Allowance of tokenIn to {spender} (0 for ETH).
* @return usdPerToken1e18In TokenIn USD price normalized to 1e18 (0 if no feed/<=0).
* @return updatedAtIn Timestamp of the last price update for tokenIn.
* @return decimals_In Decimals of tokenIn (18 for ETH; 0 if unreadable).
* @return balanceOut {wallet} balance of tokenOut (ETH if tokenOut==address(0)).
* @return allowance_Out Allowance of tokenOut to {spender} (0 for ETH).
* @return usdPerToken1e18Out TokenOut USD price normalized to 1e18 (0 if no feed/<=0).
* @return updatedAtOut Timestamp of the last price update for tokenOut.
* @return decimals_Out Decimals of tokenOut (18 for ETH; 0 if unreadable).
*/
function balanceAllowanceAndUsdDouble(
address wallet,
address spender,
address tokenIn,
bool asEthIn,
address tokenOut,
bool asEthOut
)
external
view
returns (
uint256 balanceIn,
uint256 allowance_In,
uint256 usdPerToken1e18In,
uint256 updatedAtIn,
uint8 decimals_In,
uint256 balanceOut,
uint256 allowance_Out,
uint256 usdPerToken1e18Out,
uint256 updatedAtOut,
uint8 decimals_Out
)
{
// 1) safely read balance/allowance (do not revert to non-standard ERC-20)
(balanceIn, allowance_In, decimals_In) = _safeBalanceAndAllowance(tokenIn, wallet, spender);
(balanceOut, allowance_Out, decimals_Out) = _safeBalanceAndAllowance(tokenOut, wallet, spender);
// 2) Let's try to get the price in USD (gently, without revert)
(usdPerToken1e18In, updatedAtIn) = _tryTokenUsdPrice1e18(tokenIn, asEthIn);
(usdPerToken1e18Out, updatedAtOut) = _tryTokenUsdPrice1e18(tokenOut, asEthOut);
}
/**
* @notice Return the 4 best routes (Top-1/Top-2 for 1-hop and 2-hop) and (optionally) the optimal split of the two absolute leaders.
* @param tokenIn Input token.
* @param tokenOut Output token.
* @param amountIn Input amount.
* @return best1HopRouteTop1 Payload of the best 1-hop.
* @return amountOut1HopTop1 Quote of the best 1-hop.
* @return best2HopRouteTop1 Payload of the best 2-hop.
* @return amountOut2HopTop1 Quote for the best 2-hop.
* @return best1HopRouteTop2 Payload of the second 1-hop.
* @return amountOut1HopTop2 Quote for the second 1-hop.
* @return best2HopRouteTop2 Payload of the second 2-hop.
* @return amountOut2HopTop2 Quote for the second 2-hop.
* @return splitAmountOut Best split quote between two absolute tops (0 if split does not improve).
* @return splitPercentA Share for route A (in percent, 0–100) for split (0 if split is not applicable).
*/
function getBestRoute(
address tokenIn,
address tokenOut,
uint256 amountIn
) external view returns (
bytes memory best1HopRouteTop1, uint256 amountOut1HopTop1,
bytes memory best2HopRouteTop1, uint256 amountOut2HopTop1,
bytes memory best1HopRouteTop2, uint256 amountOut1HopTop2,
bytes memory best2HopRouteTop2, uint256 amountOut2HopTop2,
uint256 splitAmountOut, uint16 splitPercentA
) {
QuoteArgs memory qa = QuoteArgs({
tokenIn: tokenIn,
tokenOut: tokenOut,
amountIn: amountIn
});
BestQuotes memory best;
TrackedRoute memory top1Overall; // Absolute best route
TrackedRoute memory top2Overall; // Second best route
for (uint256 i = 0; i < modules.length; ) {
ModuleQuotes memory quotes = _getModuleQuotes(modules[i], i, qa);
if (quotes.amountOut1Hop > 0) {
TrackedRoute memory r1 = TrackedRoute({
payload: quotes.payload1Hop,
amountOut: quotes.amountOut1Hop,
module: quotes.module,
moduleIndex: quotes.moduleIndex
});
_updateBestQuotes(best, r1, true);
(top1Overall, top2Overall) = _updateTopOverall(top1Overall, top2Overall, r1);
}
if (quotes.amountOut2Hop > 0) {
TrackedRoute memory r2 = TrackedRoute({
payload: quotes.payload2Hop,
amountOut: quotes.amountOut2Hop,
module: quotes.module,
moduleIndex: quotes.moduleIndex
});
_updateBestQuotes(best, r2, false);
(top1Overall, top2Overall) = _updateTopOverall(top1Overall, top2Overall, r2);
}
unchecked { ++i; }
}
if (top1Overall.amountOut == 0) revert NoRouteFound();
// Return the standard 8 fields
best1HopRouteTop1 = best.top1Hop.payload; amountOut1HopTop1 = best.top1Hop.amountOut;
best2HopRouteTop1 = best.top2Hop.payload; amountOut2HopTop1 = best.top2Hop.amountOut;
best1HopRouteTop2 = best.second1Hop.payload; amountOut1HopTop2 = best.second1Hop.amountOut;
best2HopRouteTop2 = best.second2Hop.payload; amountOut2HopTop2 = best.second2Hop.amountOut;
// Compute split between the two overall best routes (T1 and T2)
if (top2Overall.amountOut > 0 && keccak256(top1Overall.payload) != keccak256(top2Overall.payload)) {
(splitAmountOut, splitPercentA) = findBestSplit(
top1Overall.payload,
top2Overall.payload
);
// If split provides no improvement, do not return it,
// since the best will be either T1 or T2 (T1.amountOut >= T2.amountOut).
if (splitAmountOut <= top1Overall.amountOut) {
splitAmountOut = 0;
splitPercentA = 0;
}
} else {
// If only one route found, or T1 == T2, split is not applicable
splitAmountOut = 0;
splitPercentA = 0;
}
}
/**
* @notice Returns top quotes (1-hop & 2-hop), an optional optimal split of the two best routes,
* and wallet allowances/balances/decimals plus Chainlink USD prices.
* @dev Scans all registered modules and tracks Top-1/Top-2 for both 1-hop and 2-hop.
* Also tracks the two overall best routes and probes a split between them.
* If no route is found, reverts with {NoRouteFound}.
* For ETH inputs/outputs, balances/allowances refer to ETH and decimals=18.
* @param tokenIn Input token.
* @param tokenOut Output token.
* @param amountIn Input amount.
* @param wallet Wallet to read balances/allowances from.
* @param spender Spender to check allowances against.
* @param asEthIn If true, forces ETH/USD feed for tokenIn (WETH, stETH, etc.).
* @param asEthOut If true, forces ETH/USD feed for tokenOut (WETH, stETH, etc.).
* @return best1HopRouteTop1 Serialized payload of best 1-hop route.
* @return amountOut1HopTop1 Quoted output of best 1-hop route.
* @return best2HopRouteTop1 Serialized payload of best 2-hop route.
* @return amountOut2HopTop1 Quoted output of best 2-hop route.
* @return best1HopRouteTop2 Serialized payload of second-best 1-hop route.
* @return amountOut1HopTop2 Quoted output of second-best 1-hop route.
* @return best2HopRouteTop2 Serialized payload of second-best 2-hop route.
* @return amountOut2HopTop2 Quoted output of second-best 2-hop route.
* @return splitAmountOut Quote for the best split of the two overall leaders (0 if not improving).
* @return splitPercentA Percent for route A (0–100) in the split (0 if split not applicable).
*
* @return balanceIn {wallet} balance of tokenIn (ETH if tokenIn==address(0)).
* @return allowance_In Allowance of tokenIn to {spender} (0 for ETH).
* @return decimals_In Decimals of tokenIn (18 for ETH; 0 if unreadable).
* @return balanceOut {wallet} balance of tokenOut (ETH if tokenOut==address(0)).
* @return allowance_Out Allowance of tokenOut to {spender} (0 for ETH).
* @return decimals_Out Decimals of tokenOut (18 for ETH; 0 if unreadable).
*
* @return usdPerToken1e18In TokenIn USD price normalized to 1e18 (0 if no feed/<=0).
* @return updatedAtIn Timestamp of the last price update for tokenIn.
* @return usdPerToken1e18Out TokenOut USD price normalized to 1e18 (0 if no feed/<=0).
* @return updatedAtOut Timestamp of the last price update for tokenOut.
*/
function getBestRouteSuper(
address tokenIn,
address tokenOut,
uint256 amountIn,
address wallet,
address spender,
bool asEthIn,
bool asEthOut
) external view returns (
bytes memory best1HopRouteTop1, uint256 amountOut1HopTop1,
bytes memory best2HopRouteTop1, uint256 amountOut2HopTop1,
bytes memory best1HopRouteTop2, uint256 amountOut1HopTop2,
bytes memory best2HopRouteTop2, uint256 amountOut2HopTop2,
uint256 splitAmountOut, uint16 splitPercentA,
uint256 balanceIn, uint256 allowance_In, uint8 decimals_In,
uint256 balanceOut, uint256 allowance_Out, uint8 decimals_Out,
uint256 usdPerToken1e18In, uint256 updatedAtIn,
uint256 usdPerToken1e18Out, uint256 updatedAtOut
) {
QuoteArgs memory qa = QuoteArgs({
tokenIn: tokenIn,
tokenOut: tokenOut,
amountIn: amountIn
});
BestQuotes memory best;
TrackedRoute memory top1Overall; // Absolute best route
TrackedRoute memory top2Overall; // Second best route
for (uint256 i = 0; i < modules.length; ) {
ModuleQuotes memory quotes = _getModuleQuotes(modules[i], i, qa);
if (quotes.amountOut1Hop > 0) {
TrackedRoute memory r1 = TrackedRoute({
payload: quotes.payload1Hop,
amountOut: quotes.amountOut1Hop,
module: quotes.module,
moduleIndex: quotes.moduleIndex
});
_updateBestQuotes(best, r1, true);
(top1Overall, top2Overall) = _updateTopOverall(top1Overall, top2Overall, r1);
}
if (quotes.amountOut2Hop > 0) {
TrackedRoute memory r2 = TrackedRoute({
payload: quotes.payload2Hop,
amountOut: quotes.amountOut2Hop,
module: quotes.module,
moduleIndex: quotes.moduleIndex
});
_updateBestQuotes(best, r2, false);
(top1Overall, top2Overall) = _updateTopOverall(top1Overall, top2Overall, r2);
}
unchecked { ++i; }
}
if (top1Overall.amountOut == 0) revert NoRouteFound();
// Return the standard 8 fields
best1HopRouteTop1 = best.top1Hop.payload; amountOut1HopTop1 = best.top1Hop.amountOut;
best2HopRouteTop1 = best.top2Hop.payload; amountOut2HopTop1 = best.top2Hop.amountOut;
best1HopRouteTop2 = best.second1Hop.payload; amountOut1HopTop2 = best.second1Hop.amountOut;
best2HopRouteTop2 = best.second2Hop.payload; amountOut2HopTop2 = best.second2Hop.amountOut;
// Compute split between the two overall best routes (T1 and T2)
if (top2Overall.amountOut > 0 && keccak256(top1Overall.payload) != keccak256(top2Overall.payload)) {
(splitAmountOut, splitPercentA) = findBestSplit(
top1Overall.payload,
top2Overall.payload
);
// If split provides no improvement, do not return it,
// since the best will be either T1 or T2 (T1.amountOut >= T2.amountOut).
if (splitAmountOut <= top1Overall.amountOut) {
splitAmountOut = 0;
splitPercentA = 0;
}
} else {
// If only one route found, or T1 == T2, split is not applicable
splitAmountOut = 0;
splitPercentA = 0;
}
// safely read balance/allowance (do not revert on non-standard ERC-20)
(balanceIn, allowance_In, decimals_In) = _safeBalanceAndAllowance(tokenIn, wallet, spender);
(balanceOut, allowance_Out, decimals_Out) = _safeBalanceAndAllowance(tokenOut, wallet, spender);
// Let's try to get the price in USD (gently, without revert)
(usdPerToken1e18In, updatedAtIn) = _tryTokenUsdPrice1e18(tokenIn, asEthIn);
(usdPerToken1e18Out, updatedAtOut) = _tryTokenUsdPrice1e18(tokenOut, asEthOut);
}
/**
* @notice Find the best split ratio between two route payloads.
* @dev Discrete search by simulateRoute + local fine-tuning.
* @param payloadA Route A.
* @param payloadB Route B.
* @return bestAmountOut Best total quote.
* @return bestPercentA Share of A (0–100) giving the maximum.
*/
function findBestSplit(
bytes memory payloadA, // ИЗМЕНЕНИЕ: bytes memory
bytes memory payloadB
)
internal
view
returns (
uint256 bestAmountOut,
uint16 bestPercentA
)
{
// Decode and verify
LegDecoded memory A = _decodeRouteStruct(payloadA);
LegDecoded memory B = _decodeRouteStruct(payloadB);
require(A.amountIn > 0 && B.amountIn > 0, "UR: zero amounts");
require(A.tokenIn == B.tokenIn, "UR: in mismatch");
require(A.tokenOut == B.tokenOut, "UR: out mismatch");
require(A.amountIn == B.amountIn, "UR: totalIn mismatch");
address moduleA = A.module;
address moduleB = B.module;
// --- Step 1: Initialization (50%) ---
uint16 initialPercent = 50; // 50%
uint256 currentMaxOut = _calculateTotalOut(
moduleA, A.route, moduleB, B.route, initialPercent
);
uint16 currentBestPercent = initialPercent;
// --- Step 2: Main sparse search: 10% to 90% in 10% increments ---
// Check 10, 20, 30, 40, 60, 70, 80, 90. (50% already checked).
for (uint16 percent = 10; percent <= 90; percent += 10) {
if (percent == 50) continue;
uint256 totalOut = _calculateTotalOut(
moduleA, A.route, moduleB, B.route, percent
);
if (totalOut > currentMaxOut) {
currentMaxOut = totalOut;
currentBestPercent = percent;
}
}
// --- Step 3: Refinement (Local search, +/- 5% step) ---
uint16 bestPercentFound = currentBestPercent;
// Array of offsets for refinement: [-5, +5] Percent
int16[] memory offsets = new int16[](2);
offsets[0] = -5; // Checking -5% from the best point
offsets[1] = 5; // Checking +5% from the best point
for (uint256 i = 0; i < offsets.length; ) {
int16 offset = offsets[i];
// Protection against values exceeding the limits (e.g., below 1% or above 99%)
// Condition: bestPercentFound <= 5 (for -5) or bestPercentFound >= 95 (for +5)
if (
(offset < 0 && bestPercentFound <= uint16(-offset)) ||
(offset > 0 && bestPercentFound >= 100 - uint16(offset))
) {
unchecked { ++i; }
continue;
}
uint16 checkPercent;
if (offset < 0) {
checkPercent = bestPercentFound - uint16(-offset);
} else {
checkPercent = bestPercentFound + uint16(offset);
}
// Check that the point is within a reasonable range for swap [1, 99]
if (checkPercent >= 1 && checkPercent <= 99) {
uint256 totalOut = _calculateTotalOut(
moduleA, A.route, moduleB, B.route, checkPercent
);
if (totalOut > currentMaxOut) {
currentMaxOut = totalOut;
currentBestPercent = checkPercent;
}
}
unchecked { ++i; }
}
// 4. Return the result
bestAmountOut = currentMaxOut;
bestPercentA = currentBestPercent;
}
/**
* @notice Decodes the route payload.
* @param payload ABI-encoded packet.
* @return module Module address.
* @return moduleIndex Module index.
* @return quotedOut Output quote.
* @return tokenIn Input token.
* @return tokenOut Output token.
* @return amountIn Input amount.
* @return routeData Route byte hops.
*/
function decodeRoute(bytes calldata payload)
public
pure
returns (
address module,
uint256 moduleIndex,
uint256 quotedOut,
address tokenIn,
address tokenOut,
uint256 amountIn,
bytes[] memory routeData
)
{
(module, moduleIndex, quotedOut, tokenIn, tokenOut, amountIn, routeData) =
abi.decode(payload, (address, uint256, uint256, address, address, uint256, bytes[]));
}
/* ──────────────────────────────────── Swap ───────────────────────────────── */
/* ─────────────── ROUTE: Token → Token ───────────── */
/**
* @notice Execute a swap based on a pre-prepared payload.
* @dev Takes a commission from the swap and positive slippage; checks minAmountOut; transfers the net amount to `to`.
* @param payload ABI-encoded route (see decodeRoute).
* @param to Recipient of the final tokens.
* @param minAmountOut Minimum allowable output.
* @return netOut Net amount after commissions are deducted.
*/
function swapRoute(bytes calldata payload, address to, uint256 minAmountOut)
external
nonReentrant
returns (uint256 netOut)
{
require(to != address(0), "UR: bad to");
(
address module, , uint256 quotedOut,
address tokenIn, address tokenOut, uint256 amountIn,
bytes[] memory routeData
) = decodeRoute(payload);
require(isModule[module], "UR: unknown module");
require(amountIn > 0, "UR: zero amountIn");
require(routeData.length > 0, "UR: empty route");
IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
_smartApprove(tokenIn, module, amountIn);
uint256 amountOut = IDexModule(module).swapRoute(DexRoute({ data: routeData }), address(this), 100);
require(amountOut >= minAmountOut, "UR: slippage");
// Pay the user minus the fee — and return immediately
netOut = _distributeTokenWithFees(tokenOut, to, amountOut, quotedOut, minAmountOut);
emit SwapExecuted(module, msg.sender, to, tokenIn, tokenOut, amountIn, netOut, quotedOut);
}
/* ─────────────── ROUTE: ETH → Token ─────────────── */
/**
* @notice Swap ETH→Token by payload with WETH as tokenIn.
* @dev Wraps ETH in WETH, calls the module, holds commissions, sends net amount `to`.
* @param payload Route packet (tokenIn=WETH, amountIn=msg.value).
* @param to Recipient.
* @param minAmountOut Minimum output.
* @return netOut Net amount (ERC20).
*/
function swapRouteExactETHForTokens(
bytes calldata payload, // payload with tokenIn == WETH and amountIn == msg.value
address to,
uint256 minAmountOut
) external payable nonReentrant returns (uint256 netOut) {
require(to != address(0), "UR: bad to");
require(msg.value > 0, "UR: no ETH");
_requireWethIn(payload);
(
address module, , uint256 quotedOut,
, address tokenOut, uint256 amountIn,
bytes[] memory routeData
) = decodeRoute(payload);
require(isModule[module], "UR: unknown module");
require(routeData.length > 0, "UR: empty route");
require(amountIn == msg.value, "UR: value != amountIn");
_wrapETH(msg.value); // ETH -> WETH
_smartApprove(WETH, module, msg.value); // approve
// Send to router → calculate commission → pay customer
uint256 amountOut = IDexModule(module).swapRoute(DexRoute({data: routeData}), address(this), 100);
require(amountOut >= minAmountOut, "UR: slippage");
netOut = _distributeTokenWithFees(tokenOut, to, amountOut, quotedOut, minAmountOut);
emit SwapExecuted(module, msg.sender, to, WETH, tokenOut, amountIn, netOut, quotedOut);
}
/* ─────────────── ROUTE: Token → ETH ─────────────── */
/**
* @notice Swap Token→ETH by payload with WETH as tokenOut.
* @dev Calls the module before WETH, converts to ETH, holds commissions, sends net amount `to`.
* @param payload Route package (tokenOut=WETH).
* @param to ETH recipient.
* @param minAmountOut Minimum output.
* @return netEthOut Net amount (ETH).
*/
function swapRouteExactTokensForETH(
bytes calldata payload, // payload: tokenOut == WETH
address to,
uint256 minAmountOut
) external nonReentrant returns (uint256 netEthOut) {
require(to != address(0), "UR: bad to");
_requireWethOut(payload);
(
address module, , uint256 quotedOut,
address tokenIn, , uint256 amountIn,
bytes[] memory routeData
) = decodeRoute(payload);
require(isModule[module], "UR: unknown module");
require(amountIn > 0, "UR: zero in");
require(routeData.length > 0, "UR: empty route");
IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
_smartApprove(tokenIn, module, amountIn);
uint256 outWeth = IDexModule(module).swapRoute(DexRoute({data: routeData}), address(this), 100);
require(outWeth >= minAmountOut, "UR: slippage");
// Unwrap and distribute with fees
_unwrapWETHAndSend(outWeth, address(this));
netEthOut = _distributeETHWithFees(to, outWeth, quotedOut, minAmountOut);
emit SwapExecuted(module, msg.sender, to, tokenIn, WETH, amountIn, netEthOut, quotedOut);
}
/* ─────────────── SPLIT: Token → Token ───────────── */
/**
* @notice Perform a split swap with two token→token routes.
* @dev Splits the input by `percentA`/`100-percentA`; checks minAmountOut; holds commissions; forwards the net amount.
* @param payloadA Route A package.
* @param payloadB Route B package.
* @param percentA Share A (1–99).
* @param minAmountOut Minimum total output.
* @param to Recipient.
* @return netOut Net amount after fees.
*/
function swapSplit(
bytes calldata payloadA,
bytes calldata payloadB,
uint16 percentA,
uint256 minAmountOut,
address to
) external nonReentrant returns (uint256 netOut) {
// Decode and verify
LegDecoded memory A = _decodeRouteStructCallData(payloadA);
LegDecoded memory B = _decodeRouteStructCallData(payloadB);
require(A.amountIn > 0 && B.amountIn > 0, "UR: zero amounts");
require(A.tokenIn == B.tokenIn, "UR: in mismatch");
require(A.tokenOut == B.tokenOut, "UR: out mismatch");
require(A.amountIn == B.amountIn, "UR: totalIn mismatch");
require(A.route.length > 0 && B.route.length > 0, "UR: empty route");
require(percentA >= 1 && percentA <= 99, "UR: percent out of bounds");
require(isModule[A.module], "UR: unknown module");
require(isModule[B.module], "UR: unknown module");
IERC20(A.tokenIn).safeTransferFrom(msg.sender, address(this), A.amountIn); // if A.amountIn equals B.amountIn
_smartApprove(A.tokenIn, A.module, A.amountIn);
_smartApprove(A.tokenIn, B.module, B.amountIn);
// Perform swaps (call modules)
// Route A (percentA)
// IDexModule.swapRoute passes a percentage (0-100), and the module
// must internally calculate the exact amountIn for this part of the swap.
uint256 outA = IDexModule(A.module).swapRoute(
DexRoute({ data: A.route }),
address(this),
percentA
);
// Route B (100 - percentA)
uint256 outB = IDexModule(B.module).swapRoute(
DexRoute({ data: B.route }),
address(this),
uint16(100 - percentA)
);
// Slip check and return
require((outA + outB) >= minAmountOut, "UR: slippage");
uint256 quotedTotal = (A.quoted * percentA) / 100 + (B.quoted * (uint16(100 - percentA))) / 100;
// Commission + payment to user
netOut = _distributeTokenWithFees(A.tokenOut, to, outA + outB, quotedTotal, minAmountOut);
SplitResult memory r = SplitResult({
moduleA: A.module,
moduleB: B.module,
tokenIn: A.tokenIn,
tokenOut: A.tokenOut,
totalIn: A.amountIn,
amountInA: (A.amountIn * percentA) / 100,
amountInB: (B.amountIn * (uint16(100 - percentA))) / 100,
outA: outA,
outB: outB,
totalOut: outA + outB
});
_emitSwapSplit(r, msg.sender, to, percentA);
}
/* ─────────────── SPLIT: ETH → Token ─────────────── */
/**
* @notice Split-swap ETH→Token via two routes (both via WETH).
* @dev Converts ETH to WETH; splits input by percentage; holds fees; transfers net amount `to`.
* @param payloadA Package A (tokenIn=WETH, amountIn=msg.value).
* @param payloadB Package B.
* @param percentA Share A (1–99).
* @param minTotalOut Minimum total output.
* @param to Recipient.
* @return netOut Net result (ERC20).
*/
function swapSplitExactETHForTokens(
bytes calldata payloadA, // both: tokenIn == WETH, amountIn == msg.value
bytes calldata payloadB,
uint16 percentA, // 1..99
uint256 minTotalOut,
address to
) external payable nonReentrant returns (uint256 netOut) {
require(to != address(0), "UR: bad to");
require(msg.value > 0, "UR: no ETH");
require(percentA >= 1 && percentA <= 99, "UR: percent out of bounds");
_requireWethIn(payloadA);
_requireWethIn(payloadB);
LegDecoded memory A = _decodeRouteStructCallData(payloadA);
LegDecoded memory B = _decodeRouteStructCallData(payloadB);
require(A.amountIn == B.amountIn, "UR: split amount mismatch");
require(A.amountIn == msg.value, "UR: value != amountIn");
require(A.tokenOut == B.tokenOut, "UR: out mismatch");
require(A.route.length > 0 && B.route.length > 0, "UR: empty route");
require(isModule[A.module], "UR: unknown module");
require(isModule[B.module], "UR: unknown module");
_wrapETH(msg.value);
_smartApprove(WETH, A.module, msg.value);
_smartApprove(WETH, B.module, msg.value);
uint16 percentB = uint16(100 - percentA);
// Route execution → fees → recipient
uint256 outA = IDexModule(A.module).swapRoute(DexRoute({data: A.route}), address(this), percentA);
uint256 outB = IDexModule(B.module).swapRoute(DexRoute({data: B.route}), address(this), percentB);
uint256 grossOut = outA + outB;
require(grossOut >= minTotalOut, "UR: slippage");
uint256 quotedTotal = (A.quoted * percentA) / 100 + (B.quoted * percentB) / 100;
netOut = _distributeTokenWithFees(A.tokenOut, to, grossOut, quotedTotal, minTotalOut);
SplitResult memory r = SplitResult({
moduleA: A.module,
moduleB: B.module,
tokenIn: WETH,
tokenOut: A.tokenOut,
totalIn: msg.value,
amountInA: (uint256(msg.value) * percentA) / 100,
amountInB: (uint256(msg.value) * percentB) / 100,
outA: outA,
outB: outB,
totalOut: grossOut
});
_emitSwapSplit(r, msg.sender, to, percentA);
}
/* ─────────────── SPLIT: Token → ETH ─────────────── */
/**
* @notice Split-swap Token→ETH via two routes (both ending in WETH).
* @dev Splits input by percentage; converts WETH→ETH; holds fees; transfers net amount `to`.
* @param payloadA Package A (tokenOut=WETH).
* @param payloadB Package B.
* @param percentA Share A (1–99).
* @param minTotalEthOut Minimum total output in ETH.
* @param to ETH recipient.
* @return netEthOut Net result (ETH).
*/
function swapSplitExactTokensForETH(
bytes calldata payloadA, // both: tokenOut == WETH, same amountIn
bytes calldata payloadB,
uint16 percentA, // 1..99
uint256 minTotalEthOut,
address to
) external nonReentrant returns (uint256 netEthOut) {
require(to != address(0), "UR: bad to");
require(percentA >= 1 && percentA <= 99, "UR: percent out of bounds");
_requireWethOut(payloadA);
_requireWethOut(payloadB);
LegDecoded memory A = _decodeRouteStructCallData(payloadA);
LegDecoded memory B = _decodeRouteStructCallData(payloadB);
require(A.amountIn > 0 && B.amountIn > 0, "UR: zero in");
require(A.amountIn == B.amountIn, "UR: split amount mismatch");
require(A.tokenIn == B.tokenIn, "UR: in mismatch");
require(A.route.length > 0 && B.route.length > 0, "UR: empty route");
require(isModule[A.module], "UR: unknown module");
require(isModule[B.module], "UR: unknown module");
IERC20(A.tokenIn).safeTransferFrom(msg.sender, address(this), A.amountIn);
_smartApprove(A.tokenIn, A.module, A.amountIn);
_smartApprove(A.tokenIn, B.module, B.amountIn);
uint16 percentB = uint16(100 - percentA);
uint256 outA = IDexModule(A.module).swapRoute(DexRoute({data: A.route}), address(this), percentA);
uint256 outB = IDexModule(B.module).swapRoute(DexRoute({data: B.route}), address(this), percentB);
uint256 totalWeth = outA + outB;
require(totalWeth >= minTotalEthOut, "UR: slippage");
uint256 quotedTotal = (A.quoted * percentA) / 100 + (B.quoted * percentB) / 100;
_unwrapWETHAndSend(totalWeth, address(this));
netEthOut = _distributeETHWithFees(to, totalWeth, quotedTotal, minTotalEthOut);
SplitResult memory r = SplitResult({
moduleA: A.module,
moduleB: B.module,
tokenIn: A.tokenIn,
tokenOut: WETH,
totalIn: A.amountIn,
amountInA: (A.amountIn * percentA) / 100,
amountInB: (B.amountIn * percentB) / 100,
outA: outA,
outB: outB,
totalOut: totalWeth
});
_emitSwapSplit(r, msg.sender, to, percentA);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.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 making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)
pragma solidity >=0.6.2;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)
pragma solidity >=0.4.16;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)
pragma solidity >=0.4.16;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"viaIR": true,
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
},
"remappings": []
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address[]","name":"_modules","type":"address[]"},{"internalType":"address","name":"_feeRecipient","type":"address"},{"internalType":"uint16","name":"_feeBpsSwap","type":"uint16"},{"internalType":"uint16","name":"_feeBpsPositive","type":"uint16"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"DuplicateModule","type":"error"},{"inputs":[],"name":"NoRouteFound","type":"error"},{"inputs":[],"name":"NotAModule","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ERC20Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ETHSwept","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint16","name":"bpsSwap","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"bpsPositive","type":"uint16"}],"name":"FeeConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"module","type":"address"}],"name":"ModuleAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"module","type":"address"}],"name":"ModuleRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newCount","type":"uint256"}],"name":"ModulesReset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"module","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quotedOut","type":"uint256"}],"name":"SwapExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"moduleA","type":"address"},{"indexed":false,"internalType":"address","name":"moduleB","type":"address"},{"indexed":false,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"totalIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalOut","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"bpsA","type":"uint16"}],"name":"SwapSplitExecuted","type":"event"},{"inputs":[],"name":"MAX_FEE_POSITIVE_BPS","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_FEE_SWAP_BPS","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"module","type":"address"}],"name":"addModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"bool","name":"asEth","type":"bool"}],"name":"balanceAllowanceAndUsd","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"allowance_","type":"uint256"},{"internalType":"uint256","name":"usdPerToken1e18","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint8","name":"decimals_","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"bool","name":"asEthIn","type":"bool"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"bool","name":"asEthOut","type":"bool"}],"name":"balanceAllowanceAndUsdDouble","outputs":[{"internalType":"uint256","name":"balanceIn","type":"uint256"},{"internalType":"uint256","name":"allowance_In","type":"uint256"},{"internalType":"uint256","name":"usdPerToken1e18In","type":"uint256"},{"internalType":"uint256","name":"updatedAtIn","type":"uint256"},{"internalType":"uint8","name":"decimals_In","type":"uint8"},{"internalType":"uint256","name":"balanceOut","type":"uint256"},{"internalType":"uint256","name":"allowance_Out","type":"uint256"},{"internalType":"uint256","name":"usdPerToken1e18Out","type":"uint256"},{"internalType":"uint256","name":"updatedAtOut","type":"uint256"},{"internalType":"uint8","name":"decimals_Out","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"balanceAndAllowanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"allowance_","type":"uint256"},{"internalType":"uint8","name":"decimals_","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"decodeRoute","outputs":[{"internalType":"address","name":"module","type":"address"},{"internalType":"uint256","name":"moduleIndex","type":"uint256"},{"internalType":"uint256","name":"quotedOut","type":"uint256"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"bytes[]","name":"routeData","type":"bytes[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"feeBpsPositive","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeBpsSwap","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feedRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"getBestRoute","outputs":[{"internalType":"bytes","name":"best1HopRouteTop1","type":"bytes"},{"internalType":"uint256","name":"amountOut1HopTop1","type":"uint256"},{"internalType":"bytes","name":"best2HopRouteTop1","type":"bytes"},{"internalType":"uint256","name":"amountOut2HopTop1","type":"uint256"},{"internalType":"bytes","name":"best1HopRouteTop2","type":"bytes"},{"internalType":"uint256","name":"amountOut1HopTop2","type":"uint256"},{"internalType":"bytes","name":"best2HopRouteTop2","type":"bytes"},{"internalType":"uint256","name":"amountOut2HopTop2","type":"uint256"},{"internalType":"uint256","name":"splitAmountOut","type":"uint256"},{"internalType":"uint16","name":"splitPercentA","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"bool","name":"asEthIn","type":"bool"},{"internalType":"bool","name":"asEthOut","type":"bool"}],"name":"getBestRouteSuper","outputs":[{"internalType":"bytes","name":"best1HopRouteTop1","type":"bytes"},{"internalType":"uint256","name":"amountOut1HopTop1","type":"uint256"},{"internalType":"bytes","name":"best2HopRouteTop1","type":"bytes"},{"internalType":"uint256","name":"amountOut2HopTop1","type":"uint256"},{"internalType":"bytes","name":"best1HopRouteTop2","type":"bytes"},{"internalType":"uint256","name":"amountOut1HopTop2","type":"uint256"},{"internalType":"bytes","name":"best2HopRouteTop2","type":"bytes"},{"internalType":"uint256","name":"amountOut2HopTop2","type":"uint256"},{"internalType":"uint256","name":"splitAmountOut","type":"uint256"},{"internalType":"uint16","name":"splitPercentA","type":"uint16"},{"internalType":"uint256","name":"balanceIn","type":"uint256"},{"internalType":"uint256","name":"allowance_In","type":"uint256"},{"internalType":"uint8","name":"decimals_In","type":"uint8"},{"internalType":"uint256","name":"balanceOut","type":"uint256"},{"internalType":"uint256","name":"allowance_Out","type":"uint256"},{"internalType":"uint8","name":"decimals_Out","type":"uint8"},{"internalType":"uint256","name":"usdPerToken1e18In","type":"uint256"},{"internalType":"uint256","name":"updatedAtIn","type":"uint256"},{"internalType":"uint256","name":"usdPerToken1e18Out","type":"uint256"},{"internalType":"uint256","name":"updatedAtOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getModules","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isModule","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"modules","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"modulesLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"module","type":"address"}],"name":"removeModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_feeBpsSwap","type":"uint16"},{"internalType":"uint16","name":"_feeBpsPositive","type":"uint16"}],"name":"setFeePercents","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"setFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"reg","type":"address"}],"name":"setFeedRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_modules","type":"address[]"}],"name":"setModules","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"swapRoute","outputs":[{"internalType":"uint256","name":"netOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"swapRouteExactETHForTokens","outputs":[{"internalType":"uint256","name":"netOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"swapRouteExactTokensForETH","outputs":[{"internalType":"uint256","name":"netEthOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"payloadA","type":"bytes"},{"internalType":"bytes","name":"payloadB","type":"bytes"},{"internalType":"uint16","name":"percentA","type":"uint16"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"swapSplit","outputs":[{"internalType":"uint256","name":"netOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"payloadA","type":"bytes"},{"internalType":"bytes","name":"payloadB","type":"bytes"},{"internalType":"uint16","name":"percentA","type":"uint16"},{"internalType":"uint256","name":"minTotalOut","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"swapSplitExactETHForTokens","outputs":[{"internalType":"uint256","name":"netOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"payloadA","type":"bytes"},{"internalType":"bytes","name":"payloadB","type":"bytes"},{"internalType":"uint16","name":"percentA","type":"uint16"},{"internalType":"uint256","name":"minTotalEthOut","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"swapSplitExactTokensForETH","outputs":[{"internalType":"uint256","name":"netEthOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"sweepETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"asEth","type":"bool"}],"name":"tryTokenUsdPrice1e18","outputs":[{"internalType":"uint256","name":"usdPerToken1e18","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60806040523461046657614d45803803806100198161046a565b9283398101906080818303126104665780516001600160401b0381116104665781019180601f84011215610466578251926001600160401b038411610372578360051b9060208061006b81850161046a565b80978152019282010192831161046657602001905b82821061044e575050506100966020820161048f565b6100ae60606100a7604085016104a3565b93016104a3565b92331561043b575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a360018055600280546001600160a01b0319167347fb2585d2c56fe188d0e6ec628a38b74fceeedf1790556003545f5b8181106103fa5750506003545f600355806103b8575b508051905f5b8281106102a457505060207f24ec4b702193c608d667733c4e53752fd751d1f33023f9852db029c22a34ddf991604051908152a1606461ffff83161161025f5761271061ffff84161161021a576006805460a093841b61ffff60a01b166001600160c01b03199091166001600160a01b039093169283171760b094851b61ffff60b01b161790819055604080519382901c61ffff90811685529190941c166020830152917ffc218f700eca8d3f8834187c973fff1cd7abf19039dbd4751c024df57d7385f991a260405161487a90816104cb8239f35b60405162461bcd60e51b815260206004820152601460248201527f55523a20706f732066656520746f6f20686967680000000000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601560248201527f55523a20737761702066656520746f6f206869676800000000000000000000006044820152606490fd5b81518110156103a457600581901b8201602001516001600160a01b031690811561038657815f52600460205260ff60405f20541661039557813b1561038657815f52600460205260405f20600160ff198254161790556003549168010000000000000000831015610372576103208360018095016003556104b2565b81549060031b9083821b91868060a01b03901b1916179055600354815f52600560205260405f20557fead6a006345da1073a106d5f32372d2d2204f46cb0b4bca8f5ebafcbbed12b8a5f80a201610144565b634e487b7160e01b5f52604160045260245ffd5b63d92e233d60e01b5f5260045ffd5b633b3a461d60e21b5f5260045ffd5b634e487b7160e01b5f52603260045260245ffd5b60035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b908101905b8181106103ef575061013e565b5f81556001016103e2565b806104066001926104b2565b838060a01b0391549060031b1c16805f52600460205260405f2060ff1981541690555f5260056020525f604081205501610128565b631e4fbdf760e01b5f525f60045260245ffd5b6020809161045b8461048f565b815201910190610080565b5f80fd5b6040519190601f01601f191682016001600160401b0381118382101761037257604052565b51906001600160a01b038216820361046657565b519061ffff8216820361046657565b6003548110156103a45760035f5260205f2001905f9056fe610160604052600436101561001b575b3615610019575f80fd5b005b5f3560e01c80630197ea7c146123125780631163b2b01461223d5780631ed86f1914612214578063200f03fd146121f7578063204d406c146121aa57806323d8dbf7146120f257806324735bdf1461209f5780632fe9c11414611d2b57806335e8248f14611ced57806342f6e38914611cb05780634690484014611c885780635748108d14611b13578063715018a614611abc578063793f74e11461191257806381b2248a146118d0578063886f039a1461189057806388a77dc1146116c357806388ff8db8146116a85780638da5cb5b1461168157806390238c3914611659578063a0632461146114eb578063aaaf1c14146114cf578063ad5c4648146114a8578063b2494df3146113e0578063c7c73ee714611087578063c95cadd714610cf4578063caa4f16514610cd0578063cc654a6714610b8e578063e10f143114610a15578063e74b981b14610987578063eb2de53514610963578063efb0f22314610892578063f2fde38b1461080d578063f3f7f15b146104225763faa55b6d0361000f573461041e57606036600319011261041e576101b961237d565b6101c1612393565b604051916101ce8361262b565b6001600160a01b03908116835216602082015260443560408201526101f1612cb5565b6101f9612c91565b610201612c91565b60035490935f5b82811061033457505050602081019182511561032557805192602084519401519460408301519260208451940151602082015190606060208351930151930151936020855195015197602082015115158061030b575b156102fe5751905161026f91613d28565b969080965110156102f3575b604051998a996101408b526101408b0161029491612434565b9060208b015289810360408b01526102ab91612434565b90606089015287810360808901526102c291612434565b9060a087015285810360c08701526102d991612434565b9260e085015261010084015261ffff166101208301520390f35b5f965086955061027b565b505093505f935f9561027b565b50805160208151910120825160208151910120141561025e565b6305fcedf360e01b5f5260045ffd5b6103598282610342816125b4565b905460039190911b1c6001600160a01b0316613a48565b6060810151806103cf575b5060a08101518061037a575b5050600101610208565b966103c5929560019860808401519360208b8060a01b0382511691015191604051956103a587612646565b86526020860152604085015260608401526103c08389613c99565b613cd2565b959093905f610370565b909496610415929760408701519260018060a01b03885116602089015191604051956103fa87612646565b86526020860152604085015260608401526103c08389613c64565b9590935f610364565b5f80fd5b3461041e5761044a610450610436366124f7565b94909693979195610445613053565b613211565b95613211565b926105da60a08601918251151580610800575b61046c90612d34565b6060878101805191880151909491610491916001600160a01b03908116911614612cf6565b60808881018051918901519092916104b6916001600160a01b03908116911614612967565b878151916104ca60a0830193845114612d73565b6020898c61057e60c08201918251511515806107f2575b6104ea906129a6565b61ffff8416966001881015806107e7575b6105049061288b565b81516001600160a01b03165f9081526004875260409020546105289060ff166129e4565b516001600160a01b03165f90815260048652604090205461054b9060ff166129e4565b8b51865161056591309033906001600160a01b03166135f9565b8b5190518651916001600160a01b039182169116613370565b89518d51875161059c9290916001600160a01b039081169116613370565b8d5190516040516001600160a01b0390921691906105b9826125e0565b81525f604051809b81958294637215562960e01b8452309060048501612a9b565b03925af19485156107a6575f956107b1575b61063b9650602060018060a01b038c511660c08d01519060405191610610836125e0565b825261061b8d612a25565b915f604051809c81958294637215562960e01b8452309060048501612a9b565b03925af19687156107a6575f97610772575b50806106598888612acc565b101561066490612ad9565b8260408d01519061067491612b14565b6064900460408c01516106868c612a25565b61ffff1661069391612b14565b606490046106a091612acc565b85516001600160a01b031691908a6106b88a8a612acc565b906106c294613414565b9a5199519651935190516001600160a01b039a8b169a978816979485169490929116906106ef9083612b14565b6064900492516106fe8a612a25565b61ffff1661070b91612b14565b606490049361071a8787612acc565b976040519b6107288d61260f565b8c5260208c015260408b015260608a0152608089015260a088015260c087015260e08601526101008501526101208401523361076393613503565b60018055604051908152602090f35b9096506020813d60201161079e575b8161078e60209383612661565b8101031261041e5751958c61064d565b3d9150610781565b6040513d5f823e3d90fd5b94506020863d6020116107df575b816107cc60209383612661565b8101031261041e5761063b9551946105ec565b3d91506107bf565b5060638811156104fb565b5060c08701515115156104e1565b5060a08601511515610463565b3461041e57602036600319011261041e5761082661237d565b61082e61302d565b6001600160a01b0316801561087f575f80546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b5f525f60045260245ffd5b3461041e57602036600319011261041e576108ab61237d565b6108b361302d565b6001600160a01b03811690811561092b573b156108e6576bffffffffffffffffffffffff60a01b60025416176002555f80f35b60405162461bcd60e51b815260206004820152601b60248201527f55523a207265676973747279206e6f74206120636f6e747261637400000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601060248201526f55523a2062616420726567697374727960801b6044820152606490fd5b3461041e575f36600319011261041e57602061ffff60065460a01c16604051908152f35b3461041e57602036600319011261041e576109a061237d565b6109a861302d565b600680546001600160a01b0319166001600160a01b0392909216918217908190556040805160a083901c61ffff908116825260b09390931c90921660208301527ffc218f700eca8d3f8834187c973fff1cd7abf19039dbd4751c024df57d7385f99190819081015b0390a2005b3461041e57602036600319011261041e576004356001600160401b03811161041e573660238201121561041e578060040135906001600160401b03821161041e573660248360051b8301011161041e57610a6d61302d565b6003545f5b818110610b4d5750506003545f60035580610aed575b505f5b82811015610ac057600581901b820160240135906001600160a01b038216820361041e57610aba6001926130a9565b01610a8b565b7f24ec4b702193c608d667733c4e53752fd751d1f33023f9852db029c22a34ddf9602084604051908152a1005b60035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b017fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b818110610b425750610a88565b5f8155600101610b35565b80610b596001926125b4565b838060a01b0391549060031b1c16805f52600460205260405f2060ff1981541690555f5260056020525f604081205501610a72565b3461041e57604036600319011261041e5760043561ffff81169081810361041e576024359161ffff83169081840361041e57606490610bcb61302d565b11610c935761271010610c57576006805463ffffffff60a01b19811660a093841b61ffff60a01b161760b094851b61ffff60b01b161791829055604080519383901c61ffff90811685529290941c90911660208301526001600160a01b0316917ffc218f700eca8d3f8834187c973fff1cd7abf19039dbd4751c024df57d7385f9919081908101610a10565b60405162461bcd60e51b81526020600482015260146024820152730aaa47440e0dee640cccaca40e8dede40d0d2ced60631b6044820152606490fd5b60405162461bcd60e51b81526020600482015260156024820152740aaa47440e6eec2e040cccaca40e8dede40d0d2ced605b1b6044820152606490fd5b3461041e575f36600319011261041e57602061ffff60065460b01c16604051908152f35b3461041e57610d64610e96610d5e610d0b366124f7565b959793989291969094610d1c613053565b610d306001600160a01b0388161515612819565b61ffff88169860018a10158061107c575b610d4a9061288b565b610d548282613596565b610445848c613596565b97613211565b9460a0870190815115158061106f575b610d7d90612b45565b8151610d8f60a08901918251146128d7565b60608981018051918a0151909391610db4916001600160a01b03908116911614612cf6565b60c08a0193845151151580611061575b610dcd906129a6565b8a516001600160a01b03165f90815260046020526040902054610df29060ff166129e4565b89516001600160a01b03165f90815260046020526040902054610e179060ff166129e4565b83518151610e3191309033906001600160a01b03166135f9565b83518b518251610e4f9290916001600160a01b039081169116613370565b83518a518451610e6d9290916001600160a01b039081169116613370565b602089610e7981612a25565b8d51975160405191986001600160a01b031691906105b9826125e0565b03925af19485156107a6575f9561102b575b610eee96506020818c60c060018060a01b0382511691015160405190610ecd826125e0565b81525f604051809c81958294637215562960e01b8452309060048501612a9b565b03925af19687156107a6575f97610ff7575b50610f0b8787612acc565b97838d610f1a838c1015612ad9565b6040015190610f2891612b14565b6064900460408d01519261ffff169283610f4191612b14565b60649004610f4e91612acc565b90610f59308b613642565b610f64918a8c6137b1565b9b519a51945191516001600160a01b039b8c169b958616959093921691610f8b9084612b14565b60649004935190610f9b91612b14565b60649004936040519a610fad8c61260f565b8b5260208b015260408a0152606089015f5160206148255f395f51905f529052608089015260a088015260c087015260e08601526101008501526101208401523361076393613503565b9096506020813d602011611023575b8161101360209383612661565b8101031261041e5751958c610f00565b3d9150611006565b94506020863d602011611059575b8161104660209383612661565b8101031261041e57610eee955194610ea8565b3d9150611039565b5060c08a0151511515610dc4565b5060a08701511515610d74565b5060638a1115610d41565b3461041e5760e036600319011261041e576110a061237d565b6110a8612393565b606435916001600160a01b038316830361041e576110c46123bf565b916110cd6123f8565b9160c43593841515850361041e57604051956110e88761262b565b6001600160a01b03838116885284166020880152604435604088015261110c612cb5565b90611115612c91565b9761111e612c91565b60035490915f5b8281106113125750505060208901988951156103255783519360208551950151610120526020604082015180516101405201519260208201519060606020835193015193015193602085519501516101005260208201511515806112f8575b156112eb5751905161119591613d28565b9490809d5110156112e0575b6111ac88878b612db6565b996111ba919992988d612db6565b9d6111c7919d929c612f5d565b6080529d6111d491612f5d565b60e05260a05260405160c05260c051610280905260c051610280016111f891612434565b6101205160c0516020015260c051810360c05160400152610140519061121d91612434565b9060c0516060015260c051810360c0516080015261123a91612434565b9060c05160a0015260c051810360c05160c0015261125791612434565b986101005160c05160e0015260c051610100015261ffff1660c051610120015260c051610140015260c051610160015260ff1660c051610180015260c0516101a0015260c0516101c0015260ff1660c0516101e0015260c051610200015260805160c051610220015260a05160c051610240015260e05160c051610260015260c051900360c051f35b5f9c508c94506111a1565b50509a505f9a5f936111a1565b508051602081519101208251602081519101201415611184565b6113208282610342816125b4565b606081015180611391575b5060a081015180611341575b5050600101611125565b94611387929d6001966080840151936020898060a01b03825116910151916040519561136c87612646565b86526020860152604085015260608401526103c0838a613c99565b93909b908d611337565b94819d6113d79396604084015193602060018060a01b0382511691015191604051956113bc87612646565b86526020860152604085015260608401526103c0838a613c64565b93909b8d61132b565b3461041e575f36600319011261041e576040518060206003549283815201809260035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b905f5b818110611489575050508161143e910382612661565b604051918291602083019060208452518091526040830191905f5b818110611467575050500390f35b82516001600160a01b0316845285945060209384019390920191600101611459565b82546001600160a01b0316845260209093019260019283019201611428565b3461041e575f36600319011261041e5760206040515f5160206148255f395f51905f528152f35b3461041e575f36600319011261041e5760206040516127108152f35b3461041e57602036600319011261041e5761150461237d565b61150c61302d565b6001600160a01b03165f81815260056020526040902054801561164a575f198101818111611636576003545f198101908111611636578082036115d4575b50505060035480156115c0575f1901611562816125b4565b81546001600160a01b03600392831b1b1916909155555f818152600460209081526040808320805460ff19169055600590915281208190557f0a1ee69f55c33d8467c69ca59ce2007a737a88603d75392972520bf67cb513b89080a2005b634e487b7160e01b5f52603160045260245ffd5b6115fe916115e4611622926125b4565b905460039190911b1c6001600160a01b03169283916125b4565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b5f52600560205260405f205581808061154a565b634e487b7160e01b5f52601160045260245ffd5b632cd2a9d360e21b5f5260045ffd5b3461041e575f36600319011261041e576002546040516001600160a01b039091168152602090f35b3461041e575f36600319011261041e575f546040516001600160a01b039091168152602090f35b3461041e575f36600319011261041e57602060405160648152f35b3461041e576116fb6116d43661256e565b939092916116e0613053565b6001600160a01b038416926116f6841515612819565b6127f0565b989197909295945060018060a01b03851694855f52600460205261172560ff60405f2054166129e4565b8315611857578961175e8560209361174261178f9e5115156129a6565b6001600160a01b038b169a6117598330338f6135f9565b613370565b6040519061176b826125e0565b8152604051809b8192637215562960e01b8352606060048401526064830190612a38565b3060248301526064604483015203815f895af19889156107a6575f99611821575b509083896117cc936117c68460209d1015612ad9565b8a613414565b9560405194855260018060a01b031687850152604084015284606084015260808301527fb19d709f69027ded94fa264e8317793c05de52cf8f379f7655d49a585ce7b1f060a03393a460018055604051908152f35b9850906020893d60201161184f575b8161183d60209383612661565b8101031261041e5797519790836117b0565b3d9150611830565b60405162461bcd60e51b81526020600482015260116024820152702aa91d103d32b9379030b6b7bab73a24b760791b6044820152606490fd5b3461041e57604036600319011261041e576118ca6118ac61237d565b6118b4612393565b906118bd61302d565b6118c5613053565b612b7f565b60018055005b3461041e57602036600319011261041e5760043560035481101561041e576118f96020916125b4565b905460405160039290921b1c6001600160a01b03168152f35b3461041e576119e560206119566119283661256e565b949161193694919394613053565b6001600160a01b0384169461194c861515612819565b6116f68282613596565b91509893506119b48960018060a09a969899959a1b03881697885f526004855261198660ff60405f2054166129e4565b611991821515612b45565b61199d845115156129a6565b6001600160a01b038a16996117598330338e6135f9565b604051906119c1826125e0565b8152604051809a8192637215562960e01b8352606060048401526064830190612a38565b3060248301526064604483015203815f885af19788156107a6575f98611a86575b50908288611a2b93611a1c8460209c1015612ad9565b611a263083613642565b6137b1565b946040519384525f5160206148255f395f51905f5287850152604084015284606084015260808301527fb19d709f69027ded94fa264e8317793c05de52cf8f379f7655d49a585ce7b1f060a03393a460018055604051908152f35b9750906020883d602011611ab4575b81611aa260209383612661565b8101031261041e579651969082611a06565b3d9150611a95565b3461041e575f36600319011261041e57611ad461302d565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b611bb2611b5d6020611b243661256e565b929391611b32959195613053565b6001600160a01b03861694611b48861515612819565b611b53341515612852565b6116f68282613168565b919794509892949591506119b460018060a01b03871696875f5260048452611b8b60ff60405f2054166129e4565b611b97835115156129a6565b611ba2348c14612923565b611bab34613260565b34906132b2565b3060248301526064604483015203815f885af19788156107a6575f98611c52575b50908288611bef93611be98460209c1015612ad9565b87613414565b94604051935f5160206148255f395f51905f52855260018060a01b031687850152604084015284606084015260808301527fb19d709f69027ded94fa264e8317793c05de52cf8f379f7655d49a585ce7b1f060a03393a460018055604051908152f35b9750906020883d602011611c80575b81611c6e60209383612661565b8101031261041e579651969082611bd3565b3d9150611c61565b3461041e575f36600319011261041e576006546040516001600160a01b039091168152602090f35b3461041e57602036600319011261041e576001600160a01b03611cd161237d565b165f526004602052602060ff60405f2054166040519015158152f35b3461041e57604036600319011261041e57611d0661237d565b60243590811515820361041e57604091611d1f91612f5d565b82519182526020820152f35b611d9b611ec0610d5e611d3d366124f7565b959793989291969094611d4e613053565b611d626001600160a01b0388161515612819565b611d6d341515612852565b61ffff88169860018a101580612094575b611d879061288b565b611d918282613168565b610445848c613168565b94611dbb60a08801611db3815160a08a0151146128d7565b513414612923565b60808781018051918801519091611ddf916001600160a01b03908116911614612967565b60c08801805151151580612086575b611df7906129a6565b88516001600160a01b03165f90815260046020526040902054611e1c9060ff166129e4565b87516001600160a01b03165f90815260046020526040902054611e419060ff166129e4565b611e4a34613260565b8851611e609034906001600160a01b03166132b2565b8751611e769034906001600160a01b03166132b2565b602087611e8281612a25565b8b51935160405191946001600160a01b03169190611e9f826125e0565b81525f604051809981958294637215562960e01b8452309060048501612a9b565b03925af19283156107a6575f93612050575b611f17945060208260018060a01b038b511660c08c015160405190611ef6826125e0565b81525f604051809a81958294637215562960e01b8452309060048501612a9b565b03925af19485156107a6575f9561201c575b5086611f358686612acc565b96611f4281891015612ad9565b8260408d015190611f5291612b14565b6064900460408c01519461ffff169485611f6b91612b14565b60649004611f7891612acc565b8551611f8e938a916001600160a01b0316613414565b9951985192516001600160a01b03998a1699938416931690611fb09034612b14565b6064900491611fbf9034612b14565b606490049260405199611fd18b61260f565b8a5260208a0152604089015f5160206148255f395f51905f529052606089015234608089015260a088015260c087015260e08601526101008501526101208401523361076393613503565b9094506020813d602011612048575b8161203860209383612661565b8101031261041e5751938a611f29565b3d915061202b565b92506020843d60201161207e575b8161206b60209383612661565b8101031261041e57611f17935192611ed2565b3d915061205e565b5060c0880151511515611dee565b5060638a1115611d7e565b3461041e57602036600319011261041e576004356001600160401b03811161041e576120db6120d56120ee923690600401612407565b906127f0565b9360409795979391935197889788612458565b0390f35b3461041e5760c036600319011261041e5761014061210e61237d565b60ff61216661211b612393565b926121246123a9565b9061212d6123e9565b916121366123bf565b928561215c6121436123f8565b61216e6121518b8888612db6565b94919c90988a612db6565b9a91969097612f5d565b929098612f5d565b9790966040519b8c5260208c015260408b015260608a015216608088015260a087015260c086015260e085015261010084015216610120820152f35b3461041e57606036600319011261041e57606060ff6121e06121ca61237d565b6121d2612393565b6121da6123a9565b91612db6565b906040939293519384526020840152166040820152f35b3461041e575f36600319011261041e576020600354604051908152f35b3461041e57602036600319011261041e5761001961223061237d565b61223861302d565b6130a9565b3461041e57602036600319011261041e5761225661237d565b61225e61302d565b612266613053565b6001600160a01b03811661230d57505f546001600160a01b03165b47905f80808085855af161229361269d565b50156122d5576040519182526001600160a01b0316907fab31ddb82a9ca2de51c9befe9da7c6e815e12eac49ae58b8d9cb6b7a181cbc7190602090a260018055005b60405162461bcd60e51b815260206004820152601060248201526f115512081cddd9595c0819985a5b195960821b6044820152606490fd5b612281565b3461041e57608036600319011261041e5760a061232d61237d565b60ff61235f61233a612393565b926123556123466123a9565b61234e6123e9565b9583612db6565b9391959092612f5d565b91604051958652602086015260408501526060840152166080820152f35b600435906001600160a01b038216820361041e57565b602435906001600160a01b038216820361041e57565b604435906001600160a01b038216820361041e57565b608435906001600160a01b038216820361041e57565b35906001600160a01b038216820361041e57565b60643590811515820361041e57565b60a43590811515820361041e57565b9181601f8401121561041e578235916001600160401b03831161041e576020838186019501011161041e57565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9593919492909460e087019560018060a01b031687526020870152604086015260018060a01b0316606085015260018060a01b0316608084015260a083015260e060c0830152825180915261010082019160206101008360051b8301019401925f915b8383106124ca57505050505090565b90919293946020806124e860019360ff198682030187528951612434565b970193019301919392906124bb565b60a060031982011261041e576004356001600160401b03811161041e578161252191600401612407565b92909291602435906001600160401b03821161041e5761254391600401612407565b909160443561ffff8116810361041e5790606435906084356001600160a01b038116810361041e5790565b606060031982011261041e57600435906001600160401b03821161041e5761259891600401612407565b90916024356001600160a01b038116810361041e579060443590565b6003548110156125cc5760035f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b602081019081106001600160401b038211176125fb57604052565b634e487b7160e01b5f52604160045260245ffd5b61014081019081106001600160401b038211176125fb57604052565b606081019081106001600160401b038211176125fb57604052565b608081019081106001600160401b038211176125fb57604052565b90601f801991011681019081106001600160401b038211176125fb57604052565b6001600160401b0381116125fb57601f01601f191660200190565b3d156126c7573d906126ae82612682565b916126bc6040519384612661565b82523d5f602084013e565b606090565b6001600160401b0381116125fb5760051b60200190565b60e08183031261041e576126f6816123d5565b9260208201359260408301359261270f606082016123d5565b9261271c608083016123d5565b9260a08301359260c0810135906001600160401b03821161041e57019080601f8301121561041e57813591612750836126cc565b9261275e6040519485612661565b80845260208085019160051b8301019183831161041e5760208101915b83831061278a57505050505090565b82356001600160401b03811161041e57820185603f8201121561041e576020810135916127b683612682565b6127c36040519182612661565b838152604083850101881061041e575f60208581966040839701838601378301015281520192019161277b565b6127fc918101906126e3565b6001600160a01b0396871697959694959385169490921692909190565b1561282057565b60405162461bcd60e51b815260206004820152600a60248201526955523a2062616420746f60b01b6044820152606490fd5b1561285957565b60405162461bcd60e51b815260206004820152600a6024820152690aaa47440dcde408aa8960b31b6044820152606490fd5b1561289257565b60405162461bcd60e51b815260206004820152601960248201527f55523a2070657263656e74206f7574206f6620626f756e6473000000000000006044820152606490fd5b156128de57565b60405162461bcd60e51b815260206004820152601960248201527f55523a2073706c697420616d6f756e74206d69736d61746368000000000000006044820152606490fd5b1561292a57565b60405162461bcd60e51b81526020600482015260156024820152742aa91d103b30b63ab290109e9030b6b7bab73a24b760591b6044820152606490fd5b1561296e57565b60405162461bcd60e51b815260206004820152601060248201526f0aaa47440deeae840dad2e6dac2e8c6d60831b6044820152606490fd5b156129ad57565b60405162461bcd60e51b815260206004820152600f60248201526e55523a20656d70747920726f75746560881b6044820152606490fd5b156129eb57565b60405162461bcd60e51b815260206004820152601260248201527155523a20756e6b6e6f776e206d6f64756c6560701b6044820152606490fd5b61ffff166064039061ffff821161163657565b90602081019151916020825282518091526040820191602060408360051b8301019401925f915b838310612a6e57505050505090565b9091929394602080612a8c600193603f198682030187528951612434565b97019301930191939290612a5f565b91939261ffff90612ab6604093606086526060860190612a38565b6001600160a01b03909616602085015216910152565b9190820180921161163657565b15612ae057565b60405162461bcd60e51b815260206004820152600c60248201526b55523a20736c69707061676560a01b6044820152606490fd5b8181029291811591840414171561163657565b8115612b31570490565b634e487b7160e01b5f52601260045260245ffd5b15612b4c57565b60405162461bcd60e51b815260206004820152600b60248201526a2aa91d103d32b9379034b760a91b6044820152606490fd5b6001600160a01b0316908115612c5e576001600160a01b038116612c5857505f546001600160a01b0316905b6040516370a0823160e01b815230600482015291602083602481855afa9283156107a6575f93612c24575b508215612c1f57602081612c0c857faca8fb252cde442184e5f10e0f2e6e4029e8cd7717cae63559079610702436aa948661391b565b6040519485526001600160a01b031693a3565b505050565b9092506020813d602011612c50575b81612c4060209383612661565b8101031261041e5751915f612bd6565b3d9150612c33565b90612bab565b60405162461bcd60e51b815260206004820152600b60248201526a055523a20746f6b656e3d360ac1b6044820152606490fd5b60405190612c9e82612646565b5f6060838181528260208201528260408201520152565b60405190612cc282612646565b81612ccb612c91565b8152612cd5612c91565b6020820152612ce2612c91565b60408201526060612cf1612c91565b910152565b15612cfd57565b60405162461bcd60e51b815260206004820152600f60248201526e0aaa47440d2dc40dad2e6dac2e8c6d608b1b6044820152606490fd5b15612d3b57565b60405162461bcd60e51b815260206004820152601060248201526f55523a207a65726f20616d6f756e747360801b6044820152606490fd5b15612d7a57565b60405162461bcd60e51b81526020600482015260146024820152730aaa47440e8dee8c2d892dc40dad2e6dac2e8c6d60631b6044820152606490fd5b9192906001600160a01b03831615612f52575f8060405160208101906370a0823160e01b825260018060a01b038516602482015260248152612df9604482612661565b5190865afa612e0661269d565b9080612f46575b15612f35576020815191818082019384920101031261041e575f91612e68612e76849351975b604051636eb1769f60e11b602082019081526001600160a01b0395861660248301529490911660448201529182906064820190565b03601f198101835282612661565b5190855afa612e8361269d565b9080612f29575b15612f1f576020815191818082019384920101031261041e575f809151935b60405163313ce56760e01b602082019081526004825290612ecb602482612661565b51915afa612ed761269d565b9080612f13575b15612f0e576020815191818082019384920101031261041e575160ff811115612f08575060ff5b90565b60ff1690565b505f90565b50602081511015612ede565b505f808093612ea9565b50602081511015612e8a565b505f8091612e68612e768397612e33565b50602081511015612e0d565b3192505f9150601290565b90801561301c575b613007575b612f73816142c0565b90806130015750506001600160a01b0381165f5160206148255f395f51905f5214612ff457612fa1906143df565b81929115612fee57612fb161411a565b81929115612fe55791612fd891670de0b6b3a7640000938082105f14612fdd575094612b14565b049190565b905094612b14565b935050505f9190565b5f925090565b50612ffd61411a565b9091565b92909150565b61300f61411a565b9080613001575050612f6a565b506001600160a01b03811615612f65565b5f546001600160a01b0316330361304057565b63118cdaa760e01b5f523360045260245ffd5b600260015414613064576002600155565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b6001600160a01b03811690811561314a57815f52600460205260ff60405f20541661315957803b1561314a57815f52600460205260405f20600160ff1982541617905560035490680100000000000000008210156125fb576115fe82600161311494016003556125b4565b600354815f52600560205260405f20557fead6a006345da1073a106d5f32372d2d2204f46cb0b4bca8f5ebafcbbed12b8a5f80a2565b63d92e233d60e01b5f5260045ffd5b633b3a461d60e21b5f5260045ffd5b5f5160206148255f395f51905f5291506060013560601c0361318657565b60405162461bcd60e51b815260206004820152601b60248201527f55523a207061796c6f616420746f6b656e496e20213d205745544800000000006044820152606490fd5b6040519060e082018281106001600160401b038211176125fb57604052606060c0835f81525f60208201525f60408201525f838201525f60808201525f60a08201520152565b613229909291926132206131cb565b938101906126e3565b60c089015260a08801526001600160a01b039081166080880152908116606087015260408601919091526020850191909152168252565b5f5160206148255f395f51905f523b1561041e575f60049160405192838092630d0e30db60e41b82525f5160206148255f395f51905f525af180156107a6576132a65750565b5f6132b091612661565b565b604051636eb1769f60e11b81523060048201526001600160a01b0382166024820152916020836044815f5160206148255f395f51905f525afa9283156107a6575f9361333c575b508210613304575050565b6132b091613320575b5f5160206148255f395f51905f526145c4565b613337815f5160206148255f395f51905f52614512565b61330d565b9092506020813d602011613368575b8161335860209383612661565b8101031261041e5751915f6132f9565b3d915061334b565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301529093911690602084604481855afa9384156107a6575f946133d3575b5083106133bb57505050565b6132b092156145c4576133ce8282614512565b6145c4565b9093506020813d6020116133ff575b816133ef60209383612661565b8101031261041e5751925f6133af565b3d91506133e2565b9190820391821161163657565b93949282156134f9576132b09394958082115f146134f15750945b6006546001600160a01b03811696613452915f9182918a6134a0575b5050612acc565b92808411613498575b8361346591613407565b9583879461347e575b50506001600160a01b031661391b565b613491916001600160a01b03841661391b565b5f8061346e565b92508261345b565b61ffff8160a01c16806134e0575b5060b01c61ffff1690811515806134d7575b1561344b5761271092935087030204905f8061344b565b508088116134c0565b61271090890204925061ffff6134ae565b90509461342f565b5050509150505f90565b80516020808301516040808501516060808701516080808901516101209099015185516001600160a01b03998a16815296891697870197909752928716938501939093529185169183019190915281019390935260a083015261ffff9490941660c08201529183169216907fefb23ebb23587ddae20adda5913cf9b74002499a5f9a8ba79ed13193238a694c9060e090a3565b5f5160206148255f395f51905f5291506080013560601c036135b457565b60405162461bcd60e51b815260206004820152601c60248201527f55523a207061796c6f616420746f6b656e4f757420213d2057455448000000006044820152606490fd5b6040516323b872dd60e01b60208201526001600160a01b0392831660248201529290911660448301526064808301939093529181526132b09161363d608483612661565b6145fe565b6040516370a0823160e01b815230600482015290915f916020816024815f5160206148255f395f51905f525afa80156107a65784915f9161377c575b501061373f575f5160206148255f395f51905f523b1561041e57604051632e1a7d4d60e01b8152600481018490525f81602481835f5160206148255f395f51905f525af180156107a65761372a575b5081809381925af16136dd61269d565b50156136e557565b60405162461bcd60e51b815260206004820152601760248201527f55523a20455448207472616e73666572206661696c65640000000000000000006044820152606490fd5b6137379192505f90612661565b5f905f6136cd565b60405162461bcd60e51b81526020600482015260156024820152740aaa47440d2dce6eaccccd2c6d2cadce840ae8aa89605b1b6044820152606490fd5b9150506020813d6020116137a9575b8161379860209383612661565b8101031261041e578390515f61367e565b3d915061378b565b919093928415613913578082111561390b5750905b6006546001600160a01b038116926137e8915f918291866138ba575050612acc565b938085116138b2575b846137fb91613407565b93849281613855575b50505f80809381935af161381661269d565b501561381e57565b60405162461bcd60e51b815260206004820152600f60248201526e115512081e19995c8819985a5b1959608a1b6044820152606490fd5b5f80939450809281925af161386861269d565b50156138775782905f80613804565b60405162461bcd60e51b815260206004820152601360248201527219995948115512081e19995c8819985a5b1959606a1b6044820152606490fd5b9350836137f1565b61ffff8160a01c16806138fa575b5060b01c61ffff1690811515806138f1575b1561344b5761271092935088030204905f8061344b565b508089116138da565b612710908a0204925061ffff6138c8565b9050906137c6565b505f93505050565b60405163a9059cbb60e01b60208201526001600160a01b0390921660248301526044808301939093529181526132b09161363d606483612661565b9080601f8301121561041e5781519161396e836126cc565b9261397c6040519485612661565b80845260208085019160051b8301019183831161041e5760208101915b8383106139a857505050505090565b82516001600160401b03811161041e57820185603f8201121561041e576020810151916139d483612682565b6139e16040519182612661565b838152604083850101881061041e575f602085819660408397018386015e83010152815201920191613999565b91909160208184031261041e5760405190613a28826125e0565b819381516001600160401b03811161041e57613a449201613956565b9052565b92916040519360c085018581106001600160401b038211176125fb57604052606060408601525f6060860152606060808601525f60a08601528460018060a01b0382168082528460208301525f52600460205260ff60405f20541615613c5d575081516020808401805160408087018051915163faa55b6d60e01b9581019586526001600160a01b0396871660248201529290951660448301526064820152909492915f91829190613afd8160848101612e68565b5190865afa613b0a61269d565b90158015613c54575b613c4c5780518101916080826020850194031261041e5760208201516001600160401b03811161041e57836020613b4c92850101613a0e565b926040830151936060840151916001600160401b03831161041e578b6080613b7b8a9360208e978a0101613a0e565b9601519680151580613c41575b613bfc575b505050505082151580613bf1575b613ba9575b50505050505050565b613be095612e68948460a08c015260018060a01b039051169060018060a01b03905116915192519360405198899760208901612458565b60808301525f808080808080613ba0565b508151511515613b9b565b613c3293816060612e6894015260018060a01b038a511660018060a01b038d511690885192519360405198899760208901612458565b60408a0152865f868b82613b8d565b508351511515613b88565b505050505050565b50805115613b13565b9450505050565b602082015160208251015181115f14613c8257508051602082015252565b906020019060208251015110613c96575050565b52565b602082015190604081019160208351015181115f14613cbd57506060825191015252565b91506060019060208251015110613c96575050565b90929192613cde612c91565b50613ce7612c91565b506020840151602083015181115f14613cff57505090565b6020829592015110613d1057509190565b92509190565b60010b617fff198114611636575f0390565b613d37613d3d9193929361466a565b9261466a565b613da560a0840180511515806140c1575b613d5790612d34565b60608581015190840151613d78916001600160a01b03918216911614612cf6565b60808581015190840151613d99916001600160a01b03918216911614612967565b5160a083015114612d73565b8251815160c094850180519590930180516040516001600160a01b039485169792959490931692603292613e049291602091613de0826125e0565b8152604051809481926313c458e160e31b8352604060048401526044830190612a38565b6032602483015203818b5afa9182156107a6575f9261408b575b50613e509260209160405190613e33826125e0565b81526040516313c458e160e31b81529485928392600484016146e1565b0381865afa9081156107a6575f91614055575b613e6d9250612acc565b926032600a5b61ffff8116605a8111613edf57603214613ed457613e97818651868651918c614703565b868111613ec7575b5061ffff600a915b160161ffff811115613e7357634e487b7160e01b5f52601160045260245ffd5b955090508061ffff613e9f565b61ffff600a91613ea7565b5050604051929493919281613ef5606083612661565b600282526020820160403682378251156125cc5760041990528151600110156125cc57600560408301525f5b82518110156140485782518110156125cc5760208160051b840101518060010b5f811280809161402e575b8015614008575b613ffc5715613fd357613f669150613d16565b61ffff1661ffff85160361ffff8111611636575b61ffff811660018110159081613fc7575b50613f9d575b50600101975b97613f21565b613fad818c8b89518b5192614703565b888111613fbb575b50613f91565b9750915060015f613fb5565b6063915011155f613f8b565b5061ffff1661ffff85160161ffff811115613f7a57634e487b7160e01b5f52601160045260245ffd5b50505060010197613f97565b505f82138015613f53575061ffff614021848216612a25565b1661ffff88161015613f53565b5061ffff61403b83613d16565b1661ffff88161115613f4c565b5097505050505090509190565b90506020823d602011614083575b8161407060209383612661565b8101031261041e57613e6d915190613e63565b3d9150614063565b9091506020813d6020116140b9575b816140a760209383612661565b8101031261041e575190613e50613e1e565b3d915061409a565b5060a08301511515613d4e565b519069ffffffffffffffffffff8216820361041e57565b60ff6011199116019060ff821161163657565b60ff166012039060ff821161163657565b60ff16604d811161163657600a0a90565b6002546001600160a01b03169081156142b95760405163bcfd032d60e01b815273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6004820152610348602482015260a081604481865afa92835f925f95614263575b5061417f57505090505f905f90565b5f82131561425c57602060449160405192838092630b1c5a7560e31b825273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee600483015261034860248301525afa9081156107a6575f91614220575b5060ff811660128110156141fd5750906141f36141ee6141f9936140f8565b614109565b90612b14565b9190565b6012101561421b57906142156141ee6141f9936140e5565b90612b27565b509190565b90506020813d602011614254575b8161423b60209383612661565b8101031261041e575160ff8116810361041e575f6141cf565b3d915061422e565b50505f9190565b9250935060a0823d60a0116142b1575b8161428060a09383612661565b8101031261041e57614291826140ce565b5060208201516142a86080606085015194016140ce565b5091935f614170565b3d9150614273565b5f91508190565b6002546001600160a01b0316919082156143d75760405163bcfd032d60e01b81526001600160a01b039091166004820181905261034860248301529260a082604481845afa93845f935f96614381575b506143205750505090505f905f90565b5f83131561437957602090604460405180948193630b1c5a7560e31b8352600483015261034860248301525afa9081156107a6575f91614220575060ff811660128110156141fd5750906141f36141ee6141f9936140f8565b5050505f9190565b9350945060a0833d60a0116143cf575b8161439e60a09383612661565b8101031261041e576143af836140ce565b5060208301516143c66080606086015195016140ce565b5092945f614310565b3d9150614391565b505f91508190565b6002546001600160a01b0316919082156143d75760405163bcfd032d60e01b81526001600160a01b039091166004820181905273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee60248301529260a082604481845afa93845f935f966144bc575b506144515750505090505f905f90565b5f83131561437957602090604460405180948193630b1c5a7560e31b8352600483015273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee60248301525afa9081156107a6575f91614220575060ff811660128110156141fd5750906141f36141ee6141f9936140f8565b9350945060a0833d60a01161450a575b816144d960a09383612661565b8101031261041e576144ea836140ce565b5060208301516145016080606086015195016140ce565b5092945f614441565b3d91506144cc565b6040519060205f8184019463095ea7b360e01b865260018060a01b0316948560248601528160448601526044855261454b606486612661565b84519082855af15f513d8261459f575b50501561456757505050565b61363d6132b0936040519063095ea7b360e01b602083015260248201525f604482015260448152614599606482612661565b826145fe565b9091506145bc57506001600160a01b0381163b15155b5f8061455b565b6001146145b5565b6040519060205f8184019463095ea7b360e01b865260018060a01b031694856024860152811960448601526044855261454b606486612661565b905f602091828151910182855af1156107a6575f513d61464d57506001600160a01b0381163b155b61462d5750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415614626565b51906001600160a01b038216820361041e57565b906146736131cb565b91805181019060e08183031261041e5761468f60208201614656565b916040820151916060810151916146a860808301614656565b916146b560a08201614656565b9160c08201519160e0810151916001600160401b03831161041e57613229926020809201920101613956565b9061ffff6146fc602092959495604085526040850190612a38565b9416910152565b6147429391929460209161471682612a25565b9460405190614724826125e0565b81526040518097819482936313c458e160e31b8452600484016146e1565b03916001600160a01b03165afa9283156107a6575f936147ec575b50906147919360209260405190614773826125e0565b81526040518096819482936313c458e160e31b8452600484016146e1565b03916001600160a01b03165afa9081156107a6575f916147b6575b612f059250612acc565b90506020823d6020116147e4575b816147d160209383612661565b8101031261041e57612f059151906147ac565b3d91506147c4565b919092506020823d60201161481c575b8161480960209383612661565b8101031261041e5790519161479161475d565b3d91506147fc56fe000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a2646970667358221220ae4883b2f6d001d10dab0eb4cafb86cfab7dc8d9d456146282072e794eb0d27564736f6c634300081e00330000000000000000000000000000000000000000000000000000000000000080000000000000000000000000a68be02a2b7ebecac84a6f33f2dda63b05911b95000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000f3d3748f9093d41a380f9b94a056b05fed980d9100000000000000000000000002673b2a2f32e3f4ca1ac1cebccb6aa2c2114a2a000000000000000000000000afd80e1a3d3cf36e828a270120b12ff97006e7df000000000000000000000000bdcdabd2019f01da14620c2f95b6f4907e66eb55
Deployed Bytecode
0x610160604052600436101561001b575b3615610019575f80fd5b005b5f3560e01c80630197ea7c146123125780631163b2b01461223d5780631ed86f1914612214578063200f03fd146121f7578063204d406c146121aa57806323d8dbf7146120f257806324735bdf1461209f5780632fe9c11414611d2b57806335e8248f14611ced57806342f6e38914611cb05780634690484014611c885780635748108d14611b13578063715018a614611abc578063793f74e11461191257806381b2248a146118d0578063886f039a1461189057806388a77dc1146116c357806388ff8db8146116a85780638da5cb5b1461168157806390238c3914611659578063a0632461146114eb578063aaaf1c14146114cf578063ad5c4648146114a8578063b2494df3146113e0578063c7c73ee714611087578063c95cadd714610cf4578063caa4f16514610cd0578063cc654a6714610b8e578063e10f143114610a15578063e74b981b14610987578063eb2de53514610963578063efb0f22314610892578063f2fde38b1461080d578063f3f7f15b146104225763faa55b6d0361000f573461041e57606036600319011261041e576101b961237d565b6101c1612393565b604051916101ce8361262b565b6001600160a01b03908116835216602082015260443560408201526101f1612cb5565b6101f9612c91565b610201612c91565b60035490935f5b82811061033457505050602081019182511561032557805192602084519401519460408301519260208451940151602082015190606060208351930151930151936020855195015197602082015115158061030b575b156102fe5751905161026f91613d28565b969080965110156102f3575b604051998a996101408b526101408b0161029491612434565b9060208b015289810360408b01526102ab91612434565b90606089015287810360808901526102c291612434565b9060a087015285810360c08701526102d991612434565b9260e085015261010084015261ffff166101208301520390f35b5f965086955061027b565b505093505f935f9561027b565b50805160208151910120825160208151910120141561025e565b6305fcedf360e01b5f5260045ffd5b6103598282610342816125b4565b905460039190911b1c6001600160a01b0316613a48565b6060810151806103cf575b5060a08101518061037a575b5050600101610208565b966103c5929560019860808401519360208b8060a01b0382511691015191604051956103a587612646565b86526020860152604085015260608401526103c08389613c99565b613cd2565b959093905f610370565b909496610415929760408701519260018060a01b03885116602089015191604051956103fa87612646565b86526020860152604085015260608401526103c08389613c64565b9590935f610364565b5f80fd5b3461041e5761044a610450610436366124f7565b94909693979195610445613053565b613211565b95613211565b926105da60a08601918251151580610800575b61046c90612d34565b6060878101805191880151909491610491916001600160a01b03908116911614612cf6565b60808881018051918901519092916104b6916001600160a01b03908116911614612967565b878151916104ca60a0830193845114612d73565b6020898c61057e60c08201918251511515806107f2575b6104ea906129a6565b61ffff8416966001881015806107e7575b6105049061288b565b81516001600160a01b03165f9081526004875260409020546105289060ff166129e4565b516001600160a01b03165f90815260048652604090205461054b9060ff166129e4565b8b51865161056591309033906001600160a01b03166135f9565b8b5190518651916001600160a01b039182169116613370565b89518d51875161059c9290916001600160a01b039081169116613370565b8d5190516040516001600160a01b0390921691906105b9826125e0565b81525f604051809b81958294637215562960e01b8452309060048501612a9b565b03925af19485156107a6575f956107b1575b61063b9650602060018060a01b038c511660c08d01519060405191610610836125e0565b825261061b8d612a25565b915f604051809c81958294637215562960e01b8452309060048501612a9b565b03925af19687156107a6575f97610772575b50806106598888612acc565b101561066490612ad9565b8260408d01519061067491612b14565b6064900460408c01516106868c612a25565b61ffff1661069391612b14565b606490046106a091612acc565b85516001600160a01b031691908a6106b88a8a612acc565b906106c294613414565b9a5199519651935190516001600160a01b039a8b169a978816979485169490929116906106ef9083612b14565b6064900492516106fe8a612a25565b61ffff1661070b91612b14565b606490049361071a8787612acc565b976040519b6107288d61260f565b8c5260208c015260408b015260608a0152608089015260a088015260c087015260e08601526101008501526101208401523361076393613503565b60018055604051908152602090f35b9096506020813d60201161079e575b8161078e60209383612661565b8101031261041e5751958c61064d565b3d9150610781565b6040513d5f823e3d90fd5b94506020863d6020116107df575b816107cc60209383612661565b8101031261041e5761063b9551946105ec565b3d91506107bf565b5060638811156104fb565b5060c08701515115156104e1565b5060a08601511515610463565b3461041e57602036600319011261041e5761082661237d565b61082e61302d565b6001600160a01b0316801561087f575f80546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b5f525f60045260245ffd5b3461041e57602036600319011261041e576108ab61237d565b6108b361302d565b6001600160a01b03811690811561092b573b156108e6576bffffffffffffffffffffffff60a01b60025416176002555f80f35b60405162461bcd60e51b815260206004820152601b60248201527f55523a207265676973747279206e6f74206120636f6e747261637400000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601060248201526f55523a2062616420726567697374727960801b6044820152606490fd5b3461041e575f36600319011261041e57602061ffff60065460a01c16604051908152f35b3461041e57602036600319011261041e576109a061237d565b6109a861302d565b600680546001600160a01b0319166001600160a01b0392909216918217908190556040805160a083901c61ffff908116825260b09390931c90921660208301527ffc218f700eca8d3f8834187c973fff1cd7abf19039dbd4751c024df57d7385f99190819081015b0390a2005b3461041e57602036600319011261041e576004356001600160401b03811161041e573660238201121561041e578060040135906001600160401b03821161041e573660248360051b8301011161041e57610a6d61302d565b6003545f5b818110610b4d5750506003545f60035580610aed575b505f5b82811015610ac057600581901b820160240135906001600160a01b038216820361041e57610aba6001926130a9565b01610a8b565b7f24ec4b702193c608d667733c4e53752fd751d1f33023f9852db029c22a34ddf9602084604051908152a1005b60035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b017fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b818110610b425750610a88565b5f8155600101610b35565b80610b596001926125b4565b838060a01b0391549060031b1c16805f52600460205260405f2060ff1981541690555f5260056020525f604081205501610a72565b3461041e57604036600319011261041e5760043561ffff81169081810361041e576024359161ffff83169081840361041e57606490610bcb61302d565b11610c935761271010610c57576006805463ffffffff60a01b19811660a093841b61ffff60a01b161760b094851b61ffff60b01b161791829055604080519383901c61ffff90811685529290941c90911660208301526001600160a01b0316917ffc218f700eca8d3f8834187c973fff1cd7abf19039dbd4751c024df57d7385f9919081908101610a10565b60405162461bcd60e51b81526020600482015260146024820152730aaa47440e0dee640cccaca40e8dede40d0d2ced60631b6044820152606490fd5b60405162461bcd60e51b81526020600482015260156024820152740aaa47440e6eec2e040cccaca40e8dede40d0d2ced605b1b6044820152606490fd5b3461041e575f36600319011261041e57602061ffff60065460b01c16604051908152f35b3461041e57610d64610e96610d5e610d0b366124f7565b959793989291969094610d1c613053565b610d306001600160a01b0388161515612819565b61ffff88169860018a10158061107c575b610d4a9061288b565b610d548282613596565b610445848c613596565b97613211565b9460a0870190815115158061106f575b610d7d90612b45565b8151610d8f60a08901918251146128d7565b60608981018051918a0151909391610db4916001600160a01b03908116911614612cf6565b60c08a0193845151151580611061575b610dcd906129a6565b8a516001600160a01b03165f90815260046020526040902054610df29060ff166129e4565b89516001600160a01b03165f90815260046020526040902054610e179060ff166129e4565b83518151610e3191309033906001600160a01b03166135f9565b83518b518251610e4f9290916001600160a01b039081169116613370565b83518a518451610e6d9290916001600160a01b039081169116613370565b602089610e7981612a25565b8d51975160405191986001600160a01b031691906105b9826125e0565b03925af19485156107a6575f9561102b575b610eee96506020818c60c060018060a01b0382511691015160405190610ecd826125e0565b81525f604051809c81958294637215562960e01b8452309060048501612a9b565b03925af19687156107a6575f97610ff7575b50610f0b8787612acc565b97838d610f1a838c1015612ad9565b6040015190610f2891612b14565b6064900460408d01519261ffff169283610f4191612b14565b60649004610f4e91612acc565b90610f59308b613642565b610f64918a8c6137b1565b9b519a51945191516001600160a01b039b8c169b958616959093921691610f8b9084612b14565b60649004935190610f9b91612b14565b60649004936040519a610fad8c61260f565b8b5260208b015260408a0152606089015f5160206148255f395f51905f529052608089015260a088015260c087015260e08601526101008501526101208401523361076393613503565b9096506020813d602011611023575b8161101360209383612661565b8101031261041e5751958c610f00565b3d9150611006565b94506020863d602011611059575b8161104660209383612661565b8101031261041e57610eee955194610ea8565b3d9150611039565b5060c08a0151511515610dc4565b5060a08701511515610d74565b5060638a1115610d41565b3461041e5760e036600319011261041e576110a061237d565b6110a8612393565b606435916001600160a01b038316830361041e576110c46123bf565b916110cd6123f8565b9160c43593841515850361041e57604051956110e88761262b565b6001600160a01b03838116885284166020880152604435604088015261110c612cb5565b90611115612c91565b9761111e612c91565b60035490915f5b8281106113125750505060208901988951156103255783519360208551950151610120526020604082015180516101405201519260208201519060606020835193015193015193602085519501516101005260208201511515806112f8575b156112eb5751905161119591613d28565b9490809d5110156112e0575b6111ac88878b612db6565b996111ba919992988d612db6565b9d6111c7919d929c612f5d565b6080529d6111d491612f5d565b60e05260a05260405160c05260c051610280905260c051610280016111f891612434565b6101205160c0516020015260c051810360c05160400152610140519061121d91612434565b9060c0516060015260c051810360c0516080015261123a91612434565b9060c05160a0015260c051810360c05160c0015261125791612434565b986101005160c05160e0015260c051610100015261ffff1660c051610120015260c051610140015260c051610160015260ff1660c051610180015260c0516101a0015260c0516101c0015260ff1660c0516101e0015260c051610200015260805160c051610220015260a05160c051610240015260e05160c051610260015260c051900360c051f35b5f9c508c94506111a1565b50509a505f9a5f936111a1565b508051602081519101208251602081519101201415611184565b6113208282610342816125b4565b606081015180611391575b5060a081015180611341575b5050600101611125565b94611387929d6001966080840151936020898060a01b03825116910151916040519561136c87612646565b86526020860152604085015260608401526103c0838a613c99565b93909b908d611337565b94819d6113d79396604084015193602060018060a01b0382511691015191604051956113bc87612646565b86526020860152604085015260608401526103c0838a613c64565b93909b8d61132b565b3461041e575f36600319011261041e576040518060206003549283815201809260035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b905f5b818110611489575050508161143e910382612661565b604051918291602083019060208452518091526040830191905f5b818110611467575050500390f35b82516001600160a01b0316845285945060209384019390920191600101611459565b82546001600160a01b0316845260209093019260019283019201611428565b3461041e575f36600319011261041e5760206040515f5160206148255f395f51905f528152f35b3461041e575f36600319011261041e5760206040516127108152f35b3461041e57602036600319011261041e5761150461237d565b61150c61302d565b6001600160a01b03165f81815260056020526040902054801561164a575f198101818111611636576003545f198101908111611636578082036115d4575b50505060035480156115c0575f1901611562816125b4565b81546001600160a01b03600392831b1b1916909155555f818152600460209081526040808320805460ff19169055600590915281208190557f0a1ee69f55c33d8467c69ca59ce2007a737a88603d75392972520bf67cb513b89080a2005b634e487b7160e01b5f52603160045260245ffd5b6115fe916115e4611622926125b4565b905460039190911b1c6001600160a01b03169283916125b4565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b5f52600560205260405f205581808061154a565b634e487b7160e01b5f52601160045260245ffd5b632cd2a9d360e21b5f5260045ffd5b3461041e575f36600319011261041e576002546040516001600160a01b039091168152602090f35b3461041e575f36600319011261041e575f546040516001600160a01b039091168152602090f35b3461041e575f36600319011261041e57602060405160648152f35b3461041e576116fb6116d43661256e565b939092916116e0613053565b6001600160a01b038416926116f6841515612819565b6127f0565b989197909295945060018060a01b03851694855f52600460205261172560ff60405f2054166129e4565b8315611857578961175e8560209361174261178f9e5115156129a6565b6001600160a01b038b169a6117598330338f6135f9565b613370565b6040519061176b826125e0565b8152604051809b8192637215562960e01b8352606060048401526064830190612a38565b3060248301526064604483015203815f895af19889156107a6575f99611821575b509083896117cc936117c68460209d1015612ad9565b8a613414565b9560405194855260018060a01b031687850152604084015284606084015260808301527fb19d709f69027ded94fa264e8317793c05de52cf8f379f7655d49a585ce7b1f060a03393a460018055604051908152f35b9850906020893d60201161184f575b8161183d60209383612661565b8101031261041e5797519790836117b0565b3d9150611830565b60405162461bcd60e51b81526020600482015260116024820152702aa91d103d32b9379030b6b7bab73a24b760791b6044820152606490fd5b3461041e57604036600319011261041e576118ca6118ac61237d565b6118b4612393565b906118bd61302d565b6118c5613053565b612b7f565b60018055005b3461041e57602036600319011261041e5760043560035481101561041e576118f96020916125b4565b905460405160039290921b1c6001600160a01b03168152f35b3461041e576119e560206119566119283661256e565b949161193694919394613053565b6001600160a01b0384169461194c861515612819565b6116f68282613596565b91509893506119b48960018060a09a969899959a1b03881697885f526004855261198660ff60405f2054166129e4565b611991821515612b45565b61199d845115156129a6565b6001600160a01b038a16996117598330338e6135f9565b604051906119c1826125e0565b8152604051809a8192637215562960e01b8352606060048401526064830190612a38565b3060248301526064604483015203815f885af19788156107a6575f98611a86575b50908288611a2b93611a1c8460209c1015612ad9565b611a263083613642565b6137b1565b946040519384525f5160206148255f395f51905f5287850152604084015284606084015260808301527fb19d709f69027ded94fa264e8317793c05de52cf8f379f7655d49a585ce7b1f060a03393a460018055604051908152f35b9750906020883d602011611ab4575b81611aa260209383612661565b8101031261041e579651969082611a06565b3d9150611a95565b3461041e575f36600319011261041e57611ad461302d565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b611bb2611b5d6020611b243661256e565b929391611b32959195613053565b6001600160a01b03861694611b48861515612819565b611b53341515612852565b6116f68282613168565b919794509892949591506119b460018060a01b03871696875f5260048452611b8b60ff60405f2054166129e4565b611b97835115156129a6565b611ba2348c14612923565b611bab34613260565b34906132b2565b3060248301526064604483015203815f885af19788156107a6575f98611c52575b50908288611bef93611be98460209c1015612ad9565b87613414565b94604051935f5160206148255f395f51905f52855260018060a01b031687850152604084015284606084015260808301527fb19d709f69027ded94fa264e8317793c05de52cf8f379f7655d49a585ce7b1f060a03393a460018055604051908152f35b9750906020883d602011611c80575b81611c6e60209383612661565b8101031261041e579651969082611bd3565b3d9150611c61565b3461041e575f36600319011261041e576006546040516001600160a01b039091168152602090f35b3461041e57602036600319011261041e576001600160a01b03611cd161237d565b165f526004602052602060ff60405f2054166040519015158152f35b3461041e57604036600319011261041e57611d0661237d565b60243590811515820361041e57604091611d1f91612f5d565b82519182526020820152f35b611d9b611ec0610d5e611d3d366124f7565b959793989291969094611d4e613053565b611d626001600160a01b0388161515612819565b611d6d341515612852565b61ffff88169860018a101580612094575b611d879061288b565b611d918282613168565b610445848c613168565b94611dbb60a08801611db3815160a08a0151146128d7565b513414612923565b60808781018051918801519091611ddf916001600160a01b03908116911614612967565b60c08801805151151580612086575b611df7906129a6565b88516001600160a01b03165f90815260046020526040902054611e1c9060ff166129e4565b87516001600160a01b03165f90815260046020526040902054611e419060ff166129e4565b611e4a34613260565b8851611e609034906001600160a01b03166132b2565b8751611e769034906001600160a01b03166132b2565b602087611e8281612a25565b8b51935160405191946001600160a01b03169190611e9f826125e0565b81525f604051809981958294637215562960e01b8452309060048501612a9b565b03925af19283156107a6575f93612050575b611f17945060208260018060a01b038b511660c08c015160405190611ef6826125e0565b81525f604051809a81958294637215562960e01b8452309060048501612a9b565b03925af19485156107a6575f9561201c575b5086611f358686612acc565b96611f4281891015612ad9565b8260408d015190611f5291612b14565b6064900460408c01519461ffff169485611f6b91612b14565b60649004611f7891612acc565b8551611f8e938a916001600160a01b0316613414565b9951985192516001600160a01b03998a1699938416931690611fb09034612b14565b6064900491611fbf9034612b14565b606490049260405199611fd18b61260f565b8a5260208a0152604089015f5160206148255f395f51905f529052606089015234608089015260a088015260c087015260e08601526101008501526101208401523361076393613503565b9094506020813d602011612048575b8161203860209383612661565b8101031261041e5751938a611f29565b3d915061202b565b92506020843d60201161207e575b8161206b60209383612661565b8101031261041e57611f17935192611ed2565b3d915061205e565b5060c0880151511515611dee565b5060638a1115611d7e565b3461041e57602036600319011261041e576004356001600160401b03811161041e576120db6120d56120ee923690600401612407565b906127f0565b9360409795979391935197889788612458565b0390f35b3461041e5760c036600319011261041e5761014061210e61237d565b60ff61216661211b612393565b926121246123a9565b9061212d6123e9565b916121366123bf565b928561215c6121436123f8565b61216e6121518b8888612db6565b94919c90988a612db6565b9a91969097612f5d565b929098612f5d565b9790966040519b8c5260208c015260408b015260608a015216608088015260a087015260c086015260e085015261010084015216610120820152f35b3461041e57606036600319011261041e57606060ff6121e06121ca61237d565b6121d2612393565b6121da6123a9565b91612db6565b906040939293519384526020840152166040820152f35b3461041e575f36600319011261041e576020600354604051908152f35b3461041e57602036600319011261041e5761001961223061237d565b61223861302d565b6130a9565b3461041e57602036600319011261041e5761225661237d565b61225e61302d565b612266613053565b6001600160a01b03811661230d57505f546001600160a01b03165b47905f80808085855af161229361269d565b50156122d5576040519182526001600160a01b0316907fab31ddb82a9ca2de51c9befe9da7c6e815e12eac49ae58b8d9cb6b7a181cbc7190602090a260018055005b60405162461bcd60e51b815260206004820152601060248201526f115512081cddd9595c0819985a5b195960821b6044820152606490fd5b612281565b3461041e57608036600319011261041e5760a061232d61237d565b60ff61235f61233a612393565b926123556123466123a9565b61234e6123e9565b9583612db6565b9391959092612f5d565b91604051958652602086015260408501526060840152166080820152f35b600435906001600160a01b038216820361041e57565b602435906001600160a01b038216820361041e57565b604435906001600160a01b038216820361041e57565b608435906001600160a01b038216820361041e57565b35906001600160a01b038216820361041e57565b60643590811515820361041e57565b60a43590811515820361041e57565b9181601f8401121561041e578235916001600160401b03831161041e576020838186019501011161041e57565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9593919492909460e087019560018060a01b031687526020870152604086015260018060a01b0316606085015260018060a01b0316608084015260a083015260e060c0830152825180915261010082019160206101008360051b8301019401925f915b8383106124ca57505050505090565b90919293946020806124e860019360ff198682030187528951612434565b970193019301919392906124bb565b60a060031982011261041e576004356001600160401b03811161041e578161252191600401612407565b92909291602435906001600160401b03821161041e5761254391600401612407565b909160443561ffff8116810361041e5790606435906084356001600160a01b038116810361041e5790565b606060031982011261041e57600435906001600160401b03821161041e5761259891600401612407565b90916024356001600160a01b038116810361041e579060443590565b6003548110156125cc5760035f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b602081019081106001600160401b038211176125fb57604052565b634e487b7160e01b5f52604160045260245ffd5b61014081019081106001600160401b038211176125fb57604052565b606081019081106001600160401b038211176125fb57604052565b608081019081106001600160401b038211176125fb57604052565b90601f801991011681019081106001600160401b038211176125fb57604052565b6001600160401b0381116125fb57601f01601f191660200190565b3d156126c7573d906126ae82612682565b916126bc6040519384612661565b82523d5f602084013e565b606090565b6001600160401b0381116125fb5760051b60200190565b60e08183031261041e576126f6816123d5565b9260208201359260408301359261270f606082016123d5565b9261271c608083016123d5565b9260a08301359260c0810135906001600160401b03821161041e57019080601f8301121561041e57813591612750836126cc565b9261275e6040519485612661565b80845260208085019160051b8301019183831161041e5760208101915b83831061278a57505050505090565b82356001600160401b03811161041e57820185603f8201121561041e576020810135916127b683612682565b6127c36040519182612661565b838152604083850101881061041e575f60208581966040839701838601378301015281520192019161277b565b6127fc918101906126e3565b6001600160a01b0396871697959694959385169490921692909190565b1561282057565b60405162461bcd60e51b815260206004820152600a60248201526955523a2062616420746f60b01b6044820152606490fd5b1561285957565b60405162461bcd60e51b815260206004820152600a6024820152690aaa47440dcde408aa8960b31b6044820152606490fd5b1561289257565b60405162461bcd60e51b815260206004820152601960248201527f55523a2070657263656e74206f7574206f6620626f756e6473000000000000006044820152606490fd5b156128de57565b60405162461bcd60e51b815260206004820152601960248201527f55523a2073706c697420616d6f756e74206d69736d61746368000000000000006044820152606490fd5b1561292a57565b60405162461bcd60e51b81526020600482015260156024820152742aa91d103b30b63ab290109e9030b6b7bab73a24b760591b6044820152606490fd5b1561296e57565b60405162461bcd60e51b815260206004820152601060248201526f0aaa47440deeae840dad2e6dac2e8c6d60831b6044820152606490fd5b156129ad57565b60405162461bcd60e51b815260206004820152600f60248201526e55523a20656d70747920726f75746560881b6044820152606490fd5b156129eb57565b60405162461bcd60e51b815260206004820152601260248201527155523a20756e6b6e6f776e206d6f64756c6560701b6044820152606490fd5b61ffff166064039061ffff821161163657565b90602081019151916020825282518091526040820191602060408360051b8301019401925f915b838310612a6e57505050505090565b9091929394602080612a8c600193603f198682030187528951612434565b97019301930191939290612a5f565b91939261ffff90612ab6604093606086526060860190612a38565b6001600160a01b03909616602085015216910152565b9190820180921161163657565b15612ae057565b60405162461bcd60e51b815260206004820152600c60248201526b55523a20736c69707061676560a01b6044820152606490fd5b8181029291811591840414171561163657565b8115612b31570490565b634e487b7160e01b5f52601260045260245ffd5b15612b4c57565b60405162461bcd60e51b815260206004820152600b60248201526a2aa91d103d32b9379034b760a91b6044820152606490fd5b6001600160a01b0316908115612c5e576001600160a01b038116612c5857505f546001600160a01b0316905b6040516370a0823160e01b815230600482015291602083602481855afa9283156107a6575f93612c24575b508215612c1f57602081612c0c857faca8fb252cde442184e5f10e0f2e6e4029e8cd7717cae63559079610702436aa948661391b565b6040519485526001600160a01b031693a3565b505050565b9092506020813d602011612c50575b81612c4060209383612661565b8101031261041e5751915f612bd6565b3d9150612c33565b90612bab565b60405162461bcd60e51b815260206004820152600b60248201526a055523a20746f6b656e3d360ac1b6044820152606490fd5b60405190612c9e82612646565b5f6060838181528260208201528260408201520152565b60405190612cc282612646565b81612ccb612c91565b8152612cd5612c91565b6020820152612ce2612c91565b60408201526060612cf1612c91565b910152565b15612cfd57565b60405162461bcd60e51b815260206004820152600f60248201526e0aaa47440d2dc40dad2e6dac2e8c6d608b1b6044820152606490fd5b15612d3b57565b60405162461bcd60e51b815260206004820152601060248201526f55523a207a65726f20616d6f756e747360801b6044820152606490fd5b15612d7a57565b60405162461bcd60e51b81526020600482015260146024820152730aaa47440e8dee8c2d892dc40dad2e6dac2e8c6d60631b6044820152606490fd5b9192906001600160a01b03831615612f52575f8060405160208101906370a0823160e01b825260018060a01b038516602482015260248152612df9604482612661565b5190865afa612e0661269d565b9080612f46575b15612f35576020815191818082019384920101031261041e575f91612e68612e76849351975b604051636eb1769f60e11b602082019081526001600160a01b0395861660248301529490911660448201529182906064820190565b03601f198101835282612661565b5190855afa612e8361269d565b9080612f29575b15612f1f576020815191818082019384920101031261041e575f809151935b60405163313ce56760e01b602082019081526004825290612ecb602482612661565b51915afa612ed761269d565b9080612f13575b15612f0e576020815191818082019384920101031261041e575160ff811115612f08575060ff5b90565b60ff1690565b505f90565b50602081511015612ede565b505f808093612ea9565b50602081511015612e8a565b505f8091612e68612e768397612e33565b50602081511015612e0d565b3192505f9150601290565b90801561301c575b613007575b612f73816142c0565b90806130015750506001600160a01b0381165f5160206148255f395f51905f5214612ff457612fa1906143df565b81929115612fee57612fb161411a565b81929115612fe55791612fd891670de0b6b3a7640000938082105f14612fdd575094612b14565b049190565b905094612b14565b935050505f9190565b5f925090565b50612ffd61411a565b9091565b92909150565b61300f61411a565b9080613001575050612f6a565b506001600160a01b03811615612f65565b5f546001600160a01b0316330361304057565b63118cdaa760e01b5f523360045260245ffd5b600260015414613064576002600155565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b6001600160a01b03811690811561314a57815f52600460205260ff60405f20541661315957803b1561314a57815f52600460205260405f20600160ff1982541617905560035490680100000000000000008210156125fb576115fe82600161311494016003556125b4565b600354815f52600560205260405f20557fead6a006345da1073a106d5f32372d2d2204f46cb0b4bca8f5ebafcbbed12b8a5f80a2565b63d92e233d60e01b5f5260045ffd5b633b3a461d60e21b5f5260045ffd5b5f5160206148255f395f51905f5291506060013560601c0361318657565b60405162461bcd60e51b815260206004820152601b60248201527f55523a207061796c6f616420746f6b656e496e20213d205745544800000000006044820152606490fd5b6040519060e082018281106001600160401b038211176125fb57604052606060c0835f81525f60208201525f60408201525f838201525f60808201525f60a08201520152565b613229909291926132206131cb565b938101906126e3565b60c089015260a08801526001600160a01b039081166080880152908116606087015260408601919091526020850191909152168252565b5f5160206148255f395f51905f523b1561041e575f60049160405192838092630d0e30db60e41b82525f5160206148255f395f51905f525af180156107a6576132a65750565b5f6132b091612661565b565b604051636eb1769f60e11b81523060048201526001600160a01b0382166024820152916020836044815f5160206148255f395f51905f525afa9283156107a6575f9361333c575b508210613304575050565b6132b091613320575b5f5160206148255f395f51905f526145c4565b613337815f5160206148255f395f51905f52614512565b61330d565b9092506020813d602011613368575b8161335860209383612661565b8101031261041e5751915f6132f9565b3d915061334b565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301529093911690602084604481855afa9384156107a6575f946133d3575b5083106133bb57505050565b6132b092156145c4576133ce8282614512565b6145c4565b9093506020813d6020116133ff575b816133ef60209383612661565b8101031261041e5751925f6133af565b3d91506133e2565b9190820391821161163657565b93949282156134f9576132b09394958082115f146134f15750945b6006546001600160a01b03811696613452915f9182918a6134a0575b5050612acc565b92808411613498575b8361346591613407565b9583879461347e575b50506001600160a01b031661391b565b613491916001600160a01b03841661391b565b5f8061346e565b92508261345b565b61ffff8160a01c16806134e0575b5060b01c61ffff1690811515806134d7575b1561344b5761271092935087030204905f8061344b565b508088116134c0565b61271090890204925061ffff6134ae565b90509461342f565b5050509150505f90565b80516020808301516040808501516060808701516080808901516101209099015185516001600160a01b03998a16815296891697870197909752928716938501939093529185169183019190915281019390935260a083015261ffff9490941660c08201529183169216907fefb23ebb23587ddae20adda5913cf9b74002499a5f9a8ba79ed13193238a694c9060e090a3565b5f5160206148255f395f51905f5291506080013560601c036135b457565b60405162461bcd60e51b815260206004820152601c60248201527f55523a207061796c6f616420746f6b656e4f757420213d2057455448000000006044820152606490fd5b6040516323b872dd60e01b60208201526001600160a01b0392831660248201529290911660448301526064808301939093529181526132b09161363d608483612661565b6145fe565b6040516370a0823160e01b815230600482015290915f916020816024815f5160206148255f395f51905f525afa80156107a65784915f9161377c575b501061373f575f5160206148255f395f51905f523b1561041e57604051632e1a7d4d60e01b8152600481018490525f81602481835f5160206148255f395f51905f525af180156107a65761372a575b5081809381925af16136dd61269d565b50156136e557565b60405162461bcd60e51b815260206004820152601760248201527f55523a20455448207472616e73666572206661696c65640000000000000000006044820152606490fd5b6137379192505f90612661565b5f905f6136cd565b60405162461bcd60e51b81526020600482015260156024820152740aaa47440d2dce6eaccccd2c6d2cadce840ae8aa89605b1b6044820152606490fd5b9150506020813d6020116137a9575b8161379860209383612661565b8101031261041e578390515f61367e565b3d915061378b565b919093928415613913578082111561390b5750905b6006546001600160a01b038116926137e8915f918291866138ba575050612acc565b938085116138b2575b846137fb91613407565b93849281613855575b50505f80809381935af161381661269d565b501561381e57565b60405162461bcd60e51b815260206004820152600f60248201526e115512081e19995c8819985a5b1959608a1b6044820152606490fd5b5f80939450809281925af161386861269d565b50156138775782905f80613804565b60405162461bcd60e51b815260206004820152601360248201527219995948115512081e19995c8819985a5b1959606a1b6044820152606490fd5b9350836137f1565b61ffff8160a01c16806138fa575b5060b01c61ffff1690811515806138f1575b1561344b5761271092935088030204905f8061344b565b508089116138da565b612710908a0204925061ffff6138c8565b9050906137c6565b505f93505050565b60405163a9059cbb60e01b60208201526001600160a01b0390921660248301526044808301939093529181526132b09161363d606483612661565b9080601f8301121561041e5781519161396e836126cc565b9261397c6040519485612661565b80845260208085019160051b8301019183831161041e5760208101915b8383106139a857505050505090565b82516001600160401b03811161041e57820185603f8201121561041e576020810151916139d483612682565b6139e16040519182612661565b838152604083850101881061041e575f602085819660408397018386015e83010152815201920191613999565b91909160208184031261041e5760405190613a28826125e0565b819381516001600160401b03811161041e57613a449201613956565b9052565b92916040519360c085018581106001600160401b038211176125fb57604052606060408601525f6060860152606060808601525f60a08601528460018060a01b0382168082528460208301525f52600460205260ff60405f20541615613c5d575081516020808401805160408087018051915163faa55b6d60e01b9581019586526001600160a01b0396871660248201529290951660448301526064820152909492915f91829190613afd8160848101612e68565b5190865afa613b0a61269d565b90158015613c54575b613c4c5780518101916080826020850194031261041e5760208201516001600160401b03811161041e57836020613b4c92850101613a0e565b926040830151936060840151916001600160401b03831161041e578b6080613b7b8a9360208e978a0101613a0e565b9601519680151580613c41575b613bfc575b505050505082151580613bf1575b613ba9575b50505050505050565b613be095612e68948460a08c015260018060a01b039051169060018060a01b03905116915192519360405198899760208901612458565b60808301525f808080808080613ba0565b508151511515613b9b565b613c3293816060612e6894015260018060a01b038a511660018060a01b038d511690885192519360405198899760208901612458565b60408a0152865f868b82613b8d565b508351511515613b88565b505050505050565b50805115613b13565b9450505050565b602082015160208251015181115f14613c8257508051602082015252565b906020019060208251015110613c96575050565b52565b602082015190604081019160208351015181115f14613cbd57506060825191015252565b91506060019060208251015110613c96575050565b90929192613cde612c91565b50613ce7612c91565b506020840151602083015181115f14613cff57505090565b6020829592015110613d1057509190565b92509190565b60010b617fff198114611636575f0390565b613d37613d3d9193929361466a565b9261466a565b613da560a0840180511515806140c1575b613d5790612d34565b60608581015190840151613d78916001600160a01b03918216911614612cf6565b60808581015190840151613d99916001600160a01b03918216911614612967565b5160a083015114612d73565b8251815160c094850180519590930180516040516001600160a01b039485169792959490931692603292613e049291602091613de0826125e0565b8152604051809481926313c458e160e31b8352604060048401526044830190612a38565b6032602483015203818b5afa9182156107a6575f9261408b575b50613e509260209160405190613e33826125e0565b81526040516313c458e160e31b81529485928392600484016146e1565b0381865afa9081156107a6575f91614055575b613e6d9250612acc565b926032600a5b61ffff8116605a8111613edf57603214613ed457613e97818651868651918c614703565b868111613ec7575b5061ffff600a915b160161ffff811115613e7357634e487b7160e01b5f52601160045260245ffd5b955090508061ffff613e9f565b61ffff600a91613ea7565b5050604051929493919281613ef5606083612661565b600282526020820160403682378251156125cc5760041990528151600110156125cc57600560408301525f5b82518110156140485782518110156125cc5760208160051b840101518060010b5f811280809161402e575b8015614008575b613ffc5715613fd357613f669150613d16565b61ffff1661ffff85160361ffff8111611636575b61ffff811660018110159081613fc7575b50613f9d575b50600101975b97613f21565b613fad818c8b89518b5192614703565b888111613fbb575b50613f91565b9750915060015f613fb5565b6063915011155f613f8b565b5061ffff1661ffff85160161ffff811115613f7a57634e487b7160e01b5f52601160045260245ffd5b50505060010197613f97565b505f82138015613f53575061ffff614021848216612a25565b1661ffff88161015613f53565b5061ffff61403b83613d16565b1661ffff88161115613f4c565b5097505050505090509190565b90506020823d602011614083575b8161407060209383612661565b8101031261041e57613e6d915190613e63565b3d9150614063565b9091506020813d6020116140b9575b816140a760209383612661565b8101031261041e575190613e50613e1e565b3d915061409a565b5060a08301511515613d4e565b519069ffffffffffffffffffff8216820361041e57565b60ff6011199116019060ff821161163657565b60ff166012039060ff821161163657565b60ff16604d811161163657600a0a90565b6002546001600160a01b03169081156142b95760405163bcfd032d60e01b815273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6004820152610348602482015260a081604481865afa92835f925f95614263575b5061417f57505090505f905f90565b5f82131561425c57602060449160405192838092630b1c5a7560e31b825273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee600483015261034860248301525afa9081156107a6575f91614220575b5060ff811660128110156141fd5750906141f36141ee6141f9936140f8565b614109565b90612b14565b9190565b6012101561421b57906142156141ee6141f9936140e5565b90612b27565b509190565b90506020813d602011614254575b8161423b60209383612661565b8101031261041e575160ff8116810361041e575f6141cf565b3d915061422e565b50505f9190565b9250935060a0823d60a0116142b1575b8161428060a09383612661565b8101031261041e57614291826140ce565b5060208201516142a86080606085015194016140ce565b5091935f614170565b3d9150614273565b5f91508190565b6002546001600160a01b0316919082156143d75760405163bcfd032d60e01b81526001600160a01b039091166004820181905261034860248301529260a082604481845afa93845f935f96614381575b506143205750505090505f905f90565b5f83131561437957602090604460405180948193630b1c5a7560e31b8352600483015261034860248301525afa9081156107a6575f91614220575060ff811660128110156141fd5750906141f36141ee6141f9936140f8565b5050505f9190565b9350945060a0833d60a0116143cf575b8161439e60a09383612661565b8101031261041e576143af836140ce565b5060208301516143c66080606086015195016140ce565b5092945f614310565b3d9150614391565b505f91508190565b6002546001600160a01b0316919082156143d75760405163bcfd032d60e01b81526001600160a01b039091166004820181905273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee60248301529260a082604481845afa93845f935f966144bc575b506144515750505090505f905f90565b5f83131561437957602090604460405180948193630b1c5a7560e31b8352600483015273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee60248301525afa9081156107a6575f91614220575060ff811660128110156141fd5750906141f36141ee6141f9936140f8565b9350945060a0833d60a01161450a575b816144d960a09383612661565b8101031261041e576144ea836140ce565b5060208301516145016080606086015195016140ce565b5092945f614441565b3d91506144cc565b6040519060205f8184019463095ea7b360e01b865260018060a01b0316948560248601528160448601526044855261454b606486612661565b84519082855af15f513d8261459f575b50501561456757505050565b61363d6132b0936040519063095ea7b360e01b602083015260248201525f604482015260448152614599606482612661565b826145fe565b9091506145bc57506001600160a01b0381163b15155b5f8061455b565b6001146145b5565b6040519060205f8184019463095ea7b360e01b865260018060a01b031694856024860152811960448601526044855261454b606486612661565b905f602091828151910182855af1156107a6575f513d61464d57506001600160a01b0381163b155b61462d5750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415614626565b51906001600160a01b038216820361041e57565b906146736131cb565b91805181019060e08183031261041e5761468f60208201614656565b916040820151916060810151916146a860808301614656565b916146b560a08201614656565b9160c08201519160e0810151916001600160401b03831161041e57613229926020809201920101613956565b9061ffff6146fc602092959495604085526040850190612a38565b9416910152565b6147429391929460209161471682612a25565b9460405190614724826125e0565b81526040518097819482936313c458e160e31b8452600484016146e1565b03916001600160a01b03165afa9283156107a6575f936147ec575b50906147919360209260405190614773826125e0565b81526040518096819482936313c458e160e31b8452600484016146e1565b03916001600160a01b03165afa9081156107a6575f916147b6575b612f059250612acc565b90506020823d6020116147e4575b816147d160209383612661565b8101031261041e57612f059151906147ac565b3d91506147c4565b919092506020823d60201161481c575b8161480960209383612661565b8101031261041e5790519161479161475d565b3d91506147fc56fe000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a2646970667358221220ae4883b2f6d001d10dab0eb4cafb86cfab7dc8d9d456146282072e794eb0d27564736f6c634300081e0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000a68be02a2b7ebecac84a6f33f2dda63b05911b95000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000f3d3748f9093d41a380f9b94a056b05fed980d9100000000000000000000000002673b2a2f32e3f4ca1ac1cebccb6aa2c2114a2a000000000000000000000000afd80e1a3d3cf36e828a270120b12ff97006e7df000000000000000000000000bdcdabd2019f01da14620c2f95b6f4907e66eb55
-----Decoded View---------------
Arg [0] : _modules (address[]): 0xf3D3748f9093d41a380f9b94a056B05FED980D91,0x02673b2a2F32e3f4ca1ac1ceBCcB6aA2c2114A2a,0xAFD80E1a3d3cf36E828a270120b12FF97006E7DF,0xbdcdaBD2019f01da14620c2f95b6f4907E66EB55
Arg [1] : _feeRecipient (address): 0xa68be02a2B7EbeCac84a6F33F2DDa63B05911b95
Arg [2] : _feeBpsSwap (uint16): 5
Arg [3] : _feeBpsPositive (uint16): 10000
-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [1] : 000000000000000000000000a68be02a2b7ebecac84a6f33f2dda63b05911b95
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [3] : 0000000000000000000000000000000000000000000000000000000000002710
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [5] : 000000000000000000000000f3d3748f9093d41a380f9b94a056b05fed980d91
Arg [6] : 00000000000000000000000002673b2a2f32e3f4ca1ac1cebccb6aa2c2114a2a
Arg [7] : 000000000000000000000000afd80e1a3d3cf36e828a270120b12ff97006e7df
Arg [8] : 000000000000000000000000bdcdabd2019f01da14620c2f95b6f4907e66eb55
Deployed Bytecode Sourcemap
5079:72119:9:-:0;;;;;;;;;-1:-1:-1;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;;;;;47656:119;;5079:72119;;;;47656:119;;5079:72119;;;:::i;:::-;;;:::i;:::-;;;:::i;:::-;47976:7;5079:72119;47957:13;;5079:72119;47972:18;;;;;;49114:21;;;5079:72119;49114:21;;5079:72119;;;49114:26;49110:53;;49237:12;;:20;5079:72119;49237:20;;49279:22;;5079:72119;49332:12;5079:72119;49332:12;;;:20;5079:72119;49332:20;;49374:22;;5079:72119;;49427:15;;;:23;5079:72119;;49427:23;;49472:25;;5079:72119;49528:15;;;:23;5079:72119;49528:23;;49573:25;;5079:72119;49689:21;5079:72119;49689:21;;5079:72119;49689:25;;:93;;;47952:1146;49685:745;;;49865:19;49904;;49833:105;;;:::i;:::-;49799:139;;;5079:72119;;-1:-1:-1;50126:39:9;50122:136;;49685:745;5079:72119;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;48583:20;5079:72119;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;50122:136;5079:72119;;-1:-1:-1;5079:72119:9;;-1:-1:-1;50122:136:9;;49685:745;50368:18;;;;5079:72119;50401:17;5079:72119;49685:745;;;49689:93;49728:19;;;5079:72119;;;;;49718:30;49762:19;;5079:72119;;;;;49752:30;49718:64;;49689:93;;49110:53;55559:14;;;5079:72119;49149:14;5079:72119;;49149:14;47957:13;48038:35;48055:10;;;;;:::i;:::-;5079:72119;;47976:7;5079:72119;;;;;-1:-1:-1;;;;;5079:72119:9;48038:35;:::i;:::-;5079:72119;48094:20;;5079:72119;48094:24;48090:473;;47957:13;48583:20;;;;5079:72119;48583:24;48579:474;;47957:13;5079:72119;;;;47957:13;;48579:474;48698:18;48990:47;48698:18;;5079:72119;48698:18;;;;;5079:72119;;;;;;;;;;48850:18;;5079:72119;;;;;;;;:::i;:::-;;;;48653:235;;5079:72119;;48653:235;;5079:72119;;48653:235;;5079:72119;48935:5;;;;:::i;:::-;48990:47;:::i;:::-;48961:76;;48579:474;;;;;48090:473;48209:18;;;48500:47;48209:18;;5079:72119;48209:18;;;5079:72119;;;;;;;;;;48361:18;;5079:72119;;;;;;;;:::i;:::-;;;;48164:235;;5079:72119;;48164:235;;5079:72119;;48164:235;;5079:72119;48446:4;;;;:::i;48500:47::-;48471:76;;48090:473;;;;5079:72119;;;;;;;;68811:36;68880;5079:72119;;;:::i;:::-;2227:103:4;;;;;;;;;:::i;:::-;68811:36:9;:::i;:::-;68880;;:::i;:::-;68945:10;69986:136;68945:10;;;5079:72119;;;68945:14;;:32;;;5079:72119;68937:61;;;:::i;:::-;69017:9;;;;5079:72119;;69030:9;;;5079:72119;69017:9;;5079:72119;69009:50;;-1:-1:-1;;;;;5079:72119:9;;;;;69017:22;69009:50;:::i;:::-;69078:10;;;;5079:72119;;69092:10;;;5079:72119;69078:10;;5079:72119;69070:53;;-1:-1:-1;;;;;5079:72119:9;;;;;69078:24;69070:53;:::i;:::-;5079:72119;;;69156:10;69134:57;68945:10;69156;;5079:72119;;;69142:24;69134:57;:::i;:::-;5079:72119;69210:7;;69647:10;69210:7;;;;;;5079:72119;69210:18;;:40;;;5079:72119;69202:68;;;:::i;:::-;5079:72119;;;69289:13;69301:1;69289:13;;;:31;;;5079:72119;69281:69;;;:::i;:::-;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;69361:49;;5079:72119;;69361:49;:::i;:::-;5079:72119;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;69421:49;;5079:72119;;69421:49;:::i;:::-;5079:72119;;;;69545:10;;69538:4;;69518:10;;-1:-1:-1;;;;;5079:72119:9;69545:10;:::i;:::-;5079:72119;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;69647:10;:::i;:::-;5079:72119;;;;;;69704:10;;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;69704:10;:::i;:::-;5079:72119;;70048:7;;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;69986:136;;69538:4;69986:136;5079:72119;69986:136;;;:::i;:::-;;;;;;;;;;5079:72119;69986:136;;;5079:72119;70187:150;5079:72119;;;;;;;;;;;69210:7;70249;;;5079:72119;;;;;;;:::i;:::-;;;70311:14;;;:::i;:::-;5079:72119;;;;;;;;;;;;;70187:150;;69538:4;70187:150;5079:72119;70187:150;;;:::i;:::-;;;;;;;;;;5079:72119;70187:150;;;5079:72119;70393:11;;;;;;:::i;:::-;70392:29;;70384:54;;;:::i;:::-;70474:8;5079:72119;70474:8;;5079:72119;70474:19;;;;:::i;:::-;70311:3;5079:72119;;;70504:8;;5079:72119;70523:14;;;:::i;:::-;5079:72119;;70504:35;;;:::i;:::-;70311:3;5079:72119;;70473:73;;;:::i;:::-;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;70650:11;;;;;:::i;:::-;70609:80;;;;:::i;:::-;5079:72119;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;70930:21;;5079:72119;70930:21;:::i;:::-;70311:3;5079:72119;;;;71006:14;;;:::i;:::-;5079:72119;;70985:37;;;:::i;:::-;70311:3;5079:72119;;71104:11;;;;;:::i;:::-;5079:72119;;;;;;;:::i;:::-;;;;70725:402;;5079:72119;;70725:402;;5079:72119;69017:9;70725:402;;5079:72119;69078:10;70725:402;;5079:72119;68945:10;70725:402;;5079:72119;69210:7;70725:402;;5079:72119;;70725:402;;5079:72119;70725:402;;;5079:72119;70725:402;;;5079:72119;69518:10;71172:8;;;:::i;:::-;69301:1;1759::4;;5079:72119:9;;;;;;;;70187:150;;;;5079:72119;70187:150;;5079:72119;70187:150;;;;;;5079:72119;70187:150;;;:::i;:::-;;;5079:72119;;;;;70187:150;;;;;;;-1:-1:-1;70187:150:9;;;5079:72119;;;;;;;;;69986:136;;;5079:72119;69986:136;;5079:72119;69986:136;;;;;;5079:72119;69986:136;;;:::i;:::-;;;5079:72119;;;;70187:150;5079:72119;;69986:136;;;;;;-1:-1:-1;69986:136:9;;69289:31;-1:-1:-1;69318:2:9;69306:14;;;69289:31;;69210:40;-1:-1:-1;69210:7:9;69232;;;5079:72119;69232:18;;69210:40;;68945:32;-1:-1:-1;68945:10:9;68963;;5079:72119;68963:14;;68945:32;;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;;;:::i;:::-;1500:62:0;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;2627:22:0;;2623:91;;5079:72119:9;;;-1:-1:-1;;;;;;5079:72119:9;;;;;;-1:-1:-1;;;;;5079:72119:9;;3052:40:0;;5079:72119:9;3052:40:0;5079:72119:9;2623:91:0;2672:31;;;5079:72119:9;2672:31:0;5079:72119:9;;;;;2672:31:0;5079:72119:9;;;;;;-1:-1:-1;;5079:72119:9;;;;;;:::i;:::-;1500:62:0;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;11542:17;;5079:72119;;11599:15;:19;5079:72119;;;;;11661:18;5079:72119;;;11661:18;5079:72119;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;6291:25;5079:72119;;;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;:::i;:::-;1500:62:0;;:::i;:::-;15499:25:9;5079:72119;;-1:-1:-1;;;;;;5079:72119:9;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15540:58;;5079:72119;;;;;15540:58;;;;5079:72119;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;1500:62:0;;:::i;:::-;16978:7:9;5079:72119;;17019:5;;;;;;5079:72119;;16978:7;5079:72119;;16978:7;5079:72119;;;;17003:180;17494:9;5079:72119;17494:9;17505:5;;;;;;5079:72119;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;17540:11;5079:72119;17540:11;;:::i;:::-;5079:72119;17494:9;;17505:5;12218:29;5079:72119;17505:5;5079:72119;;;;;12218:29;5079:72119;;16978:7;5079:72119;;;;;;;;;;;;;;;;;;;;;;17008:9;17055:10;;5079:72119;17055:10;;:::i;:::-;5079:72119;;;;;;;;16978:7;5079:72119;;;;;;;;;;;;;;;;;;;;;;;;;;;;1759:1:4;5079:72119:9;17008:9;;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;;;;6770:3;1500:62:0;;;:::i;:::-;15975:35:9;5079:72119;;6838:6;-1:-1:-1;5079:72119:9;;16134:28;5079:72119;;-1:-1:-1;;;;5079:72119:9;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;16221:58;;5079:72119;;;;;16221:58;5079:72119;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;6770:3;;5079:72119;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;6770:3;;5079:72119;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;6393:29;5079:72119;;;;;;;;;;;;;;75467:36;76184:82;75398:36;5079:72119;;;:::i;:::-;2227:103:4;;;;;;;;;;;:::i;:::-;75170:39:9;-1:-1:-1;;;;;5079:72119:9;;75178:16;;75170:39;:::i;:::-;5079:72119;;;75228:13;75240:1;75228:13;;;:31;;;5079:72119;75220:69;;;:::i;:::-;75318:8;;;;:::i;:::-;75354;;;;:::i;75398:36::-;75467;;:::i;:::-;75522:10;;;;5079:72119;;;75522:14;;:32;;;5079:72119;75514:56;;;:::i;:::-;5079:72119;;75581:62;75522:10;75603;;5079:72119;;;75589:24;75581:62;:::i;:::-;75662:9;;;;5079:72119;;75675:9;;;5079:72119;75662:9;;5079:72119;75654:50;;-1:-1:-1;;;;;5079:72119:9;;;;;75662:22;75654:50;:::i;:::-;75723:7;;;;;;5079:72119;75723:18;;:40;;;5079:72119;75715:68;;;:::i;:::-;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;75794:49;;5079:72119;;75794:49;:::i;:::-;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;75854:49;;5079:72119;;75854:49;:::i;:::-;5079:72119;;;;75978:10;;75971:4;;75951:10;;-1:-1:-1;;;;;5079:72119:9;75978:10;:::i;:::-;5079:72119;;;;;;76035:10;;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;76035:10;:::i;:::-;5079:72119;;;;;;76092:10;;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;76092:10;:::i;:::-;5079:72119;76141:14;;;;:::i;:::-;5079:72119;;76231:7;;5079:72119;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;:::i;76184:82::-;;;;;;;;;;5079:72119;76184:82;;;5079:72119;76292:82;5079:72119;;;;;75723:7;5079:72119;;;;;;;;76339:7;;;5079:72119;;;;;;:::i;:::-;;;;;;;;;;;;;;;76292:82;;75971:4;76292:82;5079:72119;76292:82;;;:::i;:::-;;;;;;;;;;5079:72119;76292:82;;;5079:72119;76407:11;;;;;:::i;:::-;76437:27;;;76429:52;76437:27;;;;76429:52;:::i;:::-;5079:72119;76517:8;5079:72119;76517:19;;;;:::i;:::-;76141:3;5079:72119;;;76547:8;;5079:72119;;;;76547:19;;;;;:::i;:::-;76141:3;5079:72119;;76516:57;;;:::i;:::-;75971:4;76616:13;75971:4;76616:13;;:::i;:::-;76653:66;;;;;:::i;:::-;5079:72119;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;76954:21;;5079:72119;76954:21;:::i;:::-;76141:3;5079:72119;;;;77009:21;;;;:::i;:::-;76141:3;5079:72119;;;;;;;;;:::i;:::-;;;;76755:378;;5079:72119;;76755:378;;5079:72119;75662:9;76755:378;;-1:-1:-1;;;;;;;;;;;5079:72119:9;;76755:378;;;5079:72119;75522:10;76755:378;;5079:72119;75723:7;76755:378;;5079:72119;;76755:378;;5079:72119;76755:378;;;5079:72119;76755:378;;;5079:72119;75951:10;77178:8;;;:::i;76292:82::-;;;;5079:72119;76292:82;;5079:72119;76292:82;;;;;;5079:72119;76292:82;;;:::i;:::-;;;5079:72119;;;;;76292:82;;;;;;;-1:-1:-1;76292:82:9;;76184;;;5079:72119;76184:82;;5079:72119;76184:82;;;;;;5079:72119;76184:82;;;:::i;:::-;;;5079:72119;;;;76292:82;5079:72119;;76184:82;;;;;;-1:-1:-1;76184:82:9;;75723:40;-1:-1:-1;75723:7:9;75745;;;5079:72119;75745:18;;75723:40;;75522:32;-1:-1:-1;75522:10:9;75540;;5079:72119;75540:14;;75522:32;;75228:31;-1:-1:-1;75257:2:9;75245:14;;;75228:31;;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;;;:::i;:::-;;;:::i;:::-;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;;;;;;54066:119;;5079:72119;;;;54066:119;;5079:72119;;;:::i;:::-;54232:31;5079:72119;;:::i;:::-;54297:31;5079:72119;;:::i;:::-;54386:7;5079:72119;54367:13;;5079:72119;54382:18;;;;;;55524:21;;;5079:72119;55524:21;;5079:72119;;;55524:26;55520:53;;55647:12;;:20;5079:72119;55647:20;;55689:22;;5079:72119;;;;;55742:12;;;:20;;55722:40;;55784:22;5079:72119;55837:15;5079:72119;55837:15;;;:23;5079:72119;;55837:23;;55882:25;;5079:72119;55938:15;;;:23;5079:72119;55938:23;;55983:25;;5079:72119;;;;56099:21;;5079:72119;56099:25;;:93;;;54362:1146;56095:745;;;56275:19;56314;;56243:105;;;:::i;:::-;56209:139;;;5079:72119;;-1:-1:-1;56536:39:9;56532:136;;56095:745;56974:50;;;;;:::i;:::-;57079:51;;;;;;;;:::i;:::-;57251:39;;;;;;;:::i;:::-;;;57338:41;;;;:::i;:::-;;;;;5079:72119;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;56532:136;5079:72119;;-1:-1:-1;5079:72119:9;;-1:-1:-1;56532:136:9;;56095:745;56778:18;;;;5079:72119;56811:17;5079:72119;56095:745;;;56099:93;56138:19;;;5079:72119;;;;;56128:30;56172:19;;5079:72119;;;;;56162:30;56128:64;;56099:93;;54367:13;54448:35;54465:10;;;;;:::i;54448:35::-;5079:72119;54504:20;;5079:72119;54504:24;54500:473;;54367:13;54993:20;5079:72119;54993:20;;5079:72119;54993:24;54989:474;;54367:13;5079:72119;;;;54367:13;;54989:474;55108:18;55400:47;55108:18;;5079:72119;55108:18;5079:72119;55108:18;;;5079:72119;;;;;;;;;;55260:18;;5079:72119;;;;;;;;:::i;:::-;;;;55063:235;;5079:72119;;55063:235;;5079:72119;;55063:235;;5079:72119;55345:5;;;;:::i;55400:47::-;55371:76;;54989:474;;;;;54500:473;54619:18;;;54910:47;54619:18;;5079:72119;54619:18;;;5079:72119;;;;;;;;;;54771:18;;5079:72119;;;;;;;;:::i;:::-;;;;54574:235;;5079:72119;;54574:235;;5079:72119;;54574:235;;5079:72119;54856:4;;;;:::i;54910:47::-;54881:76;;54500:473;;;;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;;13195:7;5079:72119;;;;;;;;13195:7;5079:72119;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;-1:-1:-1;5079:72119:9;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;-1:-1:-1;;;;;;;;;;;5079:72119:9;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;6838:6;5079:72119;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;:::i;:::-;1500:62:0;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;;;18735:18;5079:72119;;;;;;18776:15;;18772:40;;-1:-1:-1;;5079:72119:9;;;;;;;18882:7;5079:72119;-1:-1:-1;;5079:72119:9;;;;;;;18917:14;;;18913:161;;5079:72119;;;;18882:7;5079:72119;;;;;-1:-1:-1;;5079:72119:9;;;;:::i;:::-;;;-1:-1:-1;;;;;18882:7:9;5079:72119;;;;;;;;;;-1:-1:-1;5079:72119:9;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;18735:18;5079:72119;;;;;1759:1:4;;;19191:21:9;;-1:-1:-1;19191:21:9;5079:72119;;;;;;;;;;;;;18913:161;18994:12;18963:16;;18994:19;18963:16;;:::i;:::-;5079:72119;;18882:7;5079:72119;;;;;-1:-1:-1;;;;;5079:72119:9;;;;18994:12;:::i;:::-;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;18994:19;5079:72119;;18735:18;5079:72119;;;;;1759:1:4;18913:161:9;;;;;5079:72119;;;;;;;;;;;;18772:40;18800:12;;;5079:72119;18800:12;5079:72119;;18800:12;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;5583:72;5079:72119;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;6770:3;5079:72119;;;;;;;63445:20;5079:72119;;;:::i;:::-;2227:103:4;;;;;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;63224:39;63232:16;;;63224:39;:::i;:::-;63445:20;:::i;:::-;5079:72119;;;;;;;;;;;;;;;;;;;;;;63478:47;5079:72119;;;;;;63478:47;:::i;:::-;63544:12;;5079:72119;;;63761:8;5079:72119;;;63589:48;5079:72119;;;63597:20;;63589:48;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;63710:8;63703:4;;63683:10;5079:72119;63710:8;:::i;:::-;63761;:::i;:::-;5079:72119;;;;;;:::i;:::-;;;;;;;;;;;;63803:79;;5079:72119;;63803:79;;5079:72119;63878:3;5079:72119;;;;:::i;:::-;63703:4;5079:72119;;;;63878:3;5079:72119;;;;63803:79;;5079:72119;63803:79;;;;;;;;5079:72119;63803:79;;;5079:72119;63901:25;;;;64031:74;63901:25;63893:50;63901:25;5079:72119;63901:25;;;63893:50;:::i;:::-;64031:74;;:::i;:::-;5079:72119;;;64123:84;5079:72119;;;;;;;;;;;;;;;;;;;;;;;;;64123:84;5079:72119;63683:10;64123:84;;1716:1:4;1759;;5079:72119:9;;;;;;63803:79;;;;5079:72119;63803:79;;5079:72119;63803:79;;;;;;5079:72119;63803:79;;;:::i;:::-;;;5079:72119;;;;;;;63803:79;5079:72119;63803:79;;;;;-1:-1:-1;63803:79:9;;5079:72119;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;2292:1:4;5079:72119:9;;:::i;:::-;;;:::i;:::-;1500:62:0;;;:::i;:::-;2227:103:4;;:::i;:::-;2292:1;:::i;:::-;1716;1759;;5079:72119:9;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;5744:24;5079:72119;5744:24;;;;;;5079:72119;5744:24;;:::i;:::-;5079:72119;;;;5744:24;5079:72119;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;67087:20;5079:72119;;;:::i;:::-;2227:103:4;;;;;;;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;66847:39;66855:16;;;66847:39;:::i;:::-;66913:7;;;;:::i;67087:20::-;5079:72119;;;;;67397:8;5079:72119;;;;;;;;;;;;;;;;;;;;;67120:47;5079:72119;;;;;;67120:47;:::i;:::-;67178:36;67186:12;;;67178:36;:::i;:::-;67225:48;5079:72119;;67233:20;;67225:48;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;67346:8;67339:4;;67319:10;5079:72119;67346:8;:::i;67397:::-;5079:72119;;;;;;:::i;:::-;;;;;;;;;;;;67437:77;;5079:72119;;67437:77;;5079:72119;67510:3;5079:72119;;;;:::i;:::-;67339:4;5079:72119;;;;67510:3;5079:72119;;;;67437:77;;5079:72119;67437:77;;;;;;;;5079:72119;67437:77;;;5079:72119;67533:23;;;;67697:60;67533:23;67525:48;67533:23;5079:72119;67533:23;;;67525:48;:::i;:::-;67658:13;67339:4;67658:13;;:::i;:::-;67697:60;:::i;:::-;5079:72119;;;67775:83;5079:72119;;-1:-1:-1;;;;;;;;;;;5079:72119:9;;;;;;;;;;;;;;;;;67775:83;5079:72119;67319:10;67775:83;;1716:1:4;1759;;5079:72119:9;;;;;;67437:77;;;;5079:72119;67437:77;;5079:72119;67437:77;;;;;;5079:72119;67437:77;;;:::i;:::-;;;5079:72119;;;;;;;67437:77;5079:72119;67437:77;;;;;-1:-1:-1;67437:77:9;;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;1500:62:0;;:::i;:::-;5079:72119:9;;;-1:-1:-1;;;;;;5079:72119:9;;;;-1:-1:-1;;;;;5079:72119:9;3052:40:0;5079:72119:9;;3052:40:0;5079:72119:9;;;65304:20;5079:72119;;;;:::i;:::-;2227:103:4;;;;;;;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;65017:39;65025:16;;;65017:39;:::i;:::-;65067:36;65075:9;:13;;65067:36;:::i;:::-;65129:7;;;;:::i;65304:20::-;5079:72119;;;;;;;;;;65617:9;5079:72119;;;;;;;;;;;;;;65337:47;5079:72119;;;;;;65337:47;:::i;:::-;65395:48;5079:72119;;65403:20;;65395:48;:::i;:::-;65454:55;65075:9;65462:21;;65454:55;:::i;:::-;65531:9;65075;65531;:::i;:::-;65075;65617;;:::i;5079:72119::-;65809:4;5079:72119;;;;65816:3;5079:72119;;;;65743:77;;5079:72119;65743:77;;;;;;;;5079:72119;65743:77;;;5079:72119;65839:25;;;;65903:74;65839:25;65831:50;65839:25;5079:72119;65839:25;;;65831:50;:::i;:::-;65903:74;;:::i;:::-;5079:72119;;;65995:81;-1:-1:-1;;;;;;;;;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;;65995:81;5079:72119;66016:10;65995:81;;1716:1:4;1759;;5079:72119:9;;;;;;65743:77;;;;5079:72119;65743:77;;5079:72119;65743:77;;;;;;5079:72119;65743:77;;;:::i;:::-;;;5079:72119;;;;;;;65743:77;5079:72119;65743:77;;;;;-1:-1:-1;65743:77:9;;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;6197:27;5079:72119;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;-1:-1:-1;;;;;5079:72119:9;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;:::i;:::-;;;;;;;;;;;;40445:35;;;;:::i;:::-;5079:72119;;;;;;;;;;;72518:36;73224:82;72449:36;5079:72119;;;:::i;:::-;2227:103:4;;;;;;;;;;;:::i;:::-;72176:39:9;-1:-1:-1;;;;;5079:72119:9;;72184:16;;72176:39;:::i;:::-;72226:36;72234:9;:13;;72226:36;:::i;:::-;5079:72119;;;72281:13;72293:1;72281:13;;;:31;;;5079:72119;72273:69;;;:::i;:::-;72370:8;;;;:::i;:::-;72405;;;;:::i;72518:36::-;72573:10;72638:57;72573:10;;;72565:62;5079:72119;;72573:10;72587;;5079:72119;72573:24;72565:62;:::i;:::-;5079:72119;72234:9;72646:23;72638:57;:::i;:::-;72714:10;;;;5079:72119;;72728:10;;;5079:72119;72714:10;;72706:53;;-1:-1:-1;;;;;5079:72119:9;;;;;72714:24;72706:53;:::i;:::-;72778:7;;;;;5079:72119;72778:18;;:40;;;5079:72119;72770:68;;;:::i;:::-;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;72849:49;;5079:72119;;72849:49;:::i;:::-;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;72909:49;;5079:72119;;72909:49;:::i;:::-;72980:9;72234;72980;:::i;:::-;5079:72119;;73031:9;;72234;;-1:-1:-1;;;;;5079:72119:9;73031:9;:::i;:::-;5079:72119;;73082:9;;72234;;-1:-1:-1;;;;;5079:72119:9;73082:9;:::i;:::-;5079:72119;73130:14;;;;:::i;:::-;5079:72119;;73271:7;;5079:72119;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;:::i;:::-;;;;;;;;;;;;;;;73224:82;;73290:4;73224:82;5079:72119;73224:82;;;:::i;:::-;;;;;;;;;;5079:72119;73224:82;;;5079:72119;73332:82;5079:72119;;;;;;;;;;;;72778:7;73379;;;5079:72119;;;;;;:::i;:::-;;;;;;;;;;;;;;;73332:82;;73290:4;73332:82;5079:72119;73332:82;;;:::i;:::-;;;;;;;;;;5079:72119;73332:82;;;5079:72119;73446:11;;;;;;:::i;:::-;73476:23;73468:48;73476:23;;;;73468:48;:::i;:::-;73552:8;5079:72119;73552:8;;5079:72119;73552:19;;;;:::i;:::-;73130:3;5079:72119;;;73582:8;;5079:72119;;;;73582:19;;;;;:::i;:::-;73130:3;5079:72119;;73551:57;;;:::i;:::-;5079:72119;;73630:76;;5079:72119;;-1:-1:-1;;;;;5079:72119:9;73630:76;:::i;:::-;5079:72119;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;73941:29;;72234:9;73941:29;:::i;:::-;73130:3;5079:72119;;;74004:29;;72234:9;74004:29;:::i;:::-;73130:3;5079:72119;;;;;;;;;:::i;:::-;;;;73742:393;;5079:72119;;73742:393;;-1:-1:-1;;;;;;;;;;;5079:72119:9;;73742:393;;;5079:72119;72234:9;72714:10;73742:393;;5079:72119;72573:10;73742:393;;5079:72119;72778:7;73742:393;;5079:72119;;73742:393;;5079:72119;73742:393;;;5079:72119;73742:393;;;5079:72119;74164:10;74180:8;;;:::i;73332:82::-;;;;5079:72119;73332:82;;5079:72119;73332:82;;;;;;5079:72119;73332:82;;;:::i;:::-;;;5079:72119;;;;;73332:82;;;;;;;-1:-1:-1;73332:82:9;;73224;;;5079:72119;73224:82;;5079:72119;73224:82;;;;;;5079:72119;73224:82;;;:::i;:::-;;;5079:72119;;;;73332:82;5079:72119;;73224:82;;;;;;-1:-1:-1;73224:82:9;;72778:40;-1:-1:-1;72778:7:9;72800;;;5079:72119;72800:18;;72778:40;;72281:31;-1:-1:-1;72310:2:9;72298:14;;;72281:31;;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;:::i;:::-;;45985:39;5079:72119;;:::i;:::-;;;;:::i;:::-;;;;:::i;:::-;;;;:::i;:::-;;;45812:51;5079:72119;;:::i;:::-;46072:41;45707:50;;;;;:::i;:::-;45812:51;;;;;;;:::i;:::-;45985:39;;;;;;:::i;:::-;46072:41;;;;:::i;:::-;5079:72119;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;41486:48;5079:72119;;:::i;:::-;;;:::i;:::-;;;:::i;:::-;41486:48;;:::i;:::-;5079:72119;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;12895:7;5079:72119;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;12451:6;5079:72119;;:::i;:::-;1500:62:0;;:::i;:::-;12451:6:9;:::i;5079:72119::-;;;;;;-1:-1:-1;;5079:72119:9;;;;;;:::i;:::-;1500:62:0;;:::i;:::-;2227:103:4;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;;-1:-1:-1;5079:72119:9;;-1:-1:-1;;;;;5079:72119:9;14835:33;14893:21;14938:30;5079:72119;14938:30;;;;;;;;;:::i;:::-;;5079:72119;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;15026:24;;5079:72119;;15026:24;1716:1:4;1759;;5079:72119:9;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;14835:33;;;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;:::i;:::-;;43236:35;5079:72119;;:::i;:::-;;43070:48;5079:72119;;:::i;:::-;;;:::i;:::-;43070:48;;;:::i;:::-;43236:35;;;;;;:::i;:::-;5079:72119;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;:::o;:::-;;;-1:-1:-1;;;;;5079:72119:9;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;-1:-1:-1;5079:72119:9;;;;;;;;-1:-1:-1;;5079:72119:9;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;:::i;:::-;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;:::o;:::-;;-1:-1:-1;;5079:72119:9;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;:::i;:::-;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;:::o;:::-;5744:24;5079:72119;;;;;;5744:24;-1:-1:-1;5079:72119:9;;-1:-1:-1;5079:72119:9;;;-1:-1:-1;5079:72119:9;:::o;:::-;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;:::o;:::-;;;;-1:-1:-1;5079:72119:9;;;;;-1:-1:-1;5079:72119:9;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;:::o;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;:::o;:::-;-1:-1:-1;;;;;5079:72119:9;;;;;;-1:-1:-1;;5079:72119:9;;;;:::o;:::-;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;-1:-1:-1;5079:72119:9;;;;:::o;:::-;;;:::o;:::-;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;:::o;:::-;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;5079:72119:9;;-1:-1:-1;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;61689:526;62123:84;61689:526;62123:84;;;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;;62035:172;;;;5079:72119;;;;;;;;62035:172;;;61689:526::o;5079:72119::-;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;13833:460;-1:-1:-1;;;;;5079:72119:9;;13965:19;;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;-1:-1:-1;13982:1:9;5079:72119;-1:-1:-1;;;;;5079:72119:9;;14031:33;5079:72119;;-1:-1:-1;;;14091:38:9;;14123:4;14091:38;;;5079:72119;;;;14091:38;5079:72119;14091:38;;;;;;;;13982:1;14091:38;;;14031:33;14144:8;;;14140:21;;5079:72119;14228:3;;;14248:37;14228:3;;;:::i;:::-;5079:72119;;;;;-1:-1:-1;;;;;5079:72119:9;;14248:37;13833:460::o;14140:21::-;14154:7;;;:::o;14091:38::-;;;;5079:72119;14091:38;;5079:72119;14091:38;;;;;;5079:72119;14091:38;;;:::i;:::-;;;5079:72119;;;;;14091:38;;;;;;;-1:-1:-1;14091:38:9;;14031:33;;;;5079:72119;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;5079:72119:9;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;:::o;:::-;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;34525:1529;;;;-1:-1:-1;;;;;5079:72119:9;;34720:19;34716:128;;34737:1;5079:72119;;;34949:57;;;5079:72119;;;;34949:57;;5079:72119;;;;;;;34949:57;;;5079:72119;34949:57;;;;;;;:::i;:::-;34932:75;;;;;;;:::i;:::-;35022:25;;;;34525:1529;35018:132;;;5079:72119;;;35070:28;;;;;;;;;;5079:72119;;;;34737:1;5079:72119;35264:66;;5079:72119;;;35018:132;;5079:72119;;-1:-1:-1;;;34949:57:9;35264:66;;;;;-1:-1:-1;;;;;5079:72119:9;;;34949:57;35264:66;;5079:72119;;;;;;;;;;;;;;;;;35264:66;;5079:72119;;35264:66;;;;;;:::i;:::-;35247:84;;;;;;;:::i;:::-;35346:25;;;;35018:132;35342:138;;;5079:72119;;;35397:28;;;;;;;;;;5079:72119;;;;34737:1;5079:72119;;;35342:138;;5079:72119;;-1:-1:-1;;;34949:57:9;35600:42;;;;;;;;;;34949:57;5079:72119;35600:42;:::i;:::-;35583:60;;;;;;:::i;:::-;35658:25;;;;35342:138;35654:393;;;5079:72119;;;35820:28;;;;;;;;;;5079:72119;;;;;35874:3;35870:7;;35874:3;;;35870:31;35874:3;35870:31;35654:393;34525:1529::o;35870:31::-;35874:3;5079:72119;35654:393;34525:1529::o;35654:393::-;36027:8;34737:1;35654:393;34525:1529::o;35658:25::-;5079:72119;34949:57;5079:72119;;35665:18;;35658:25;;35342:138;35458:10;34737:1;35458:10;;35342:138;;;35346:25;5079:72119;34949:57;5079:72119;;35353:18;;35346:25;;35018:132;35131:7;34737:1;35131:7;;35264:66;;35131:7;35018:132;;;35022:25;5079:72119;34949:57;5079:72119;;35029:18;;35022:25;;34716:128;34810:14;;-1:-1:-1;34737:1:9;;-1:-1:-1;34829:2:9;;34802:30::o;38348:1160::-;;38549:28;;;;38348:1160;38545:186;;38348:1160;38797:32;;;:::i;:::-;38844:14;;38840:49;;-1:-1:-1;;;;;;;5079:72119:9;;-1:-1:-1;;;;;;;;;;;38935:13:9;38931:151;;39195:32;;;:::i;:::-;39242:9;;;;39238:31;;39313:36;;:::i;:::-;39364:11;;;;39360:33;;39423:25;39470:13;39423:25;39487:4;39423:25;:11;;;:25;:11;;;:25;;39470:13;:::i;:::-;5079:72119;39461:39;38348:1160;:::o;39423:25::-;;;;39470:13;:::i;39360:33::-;39377:16;;;;38857:1;39377:16;;:::o;39238:31::-;38857:1;;-1:-1:-1;39253:16:9;:::o;38931:151::-;38990:36;;;:::i;:::-;39041:29;;:::o;38840:49::-;38860:29;;;-1:-1:-1;38860:29:9:o;38545:186::-;38619:36;;:::i;:::-;38674:14;;38670:49;;38545:186;;;;38549:28;-1:-1:-1;;;;;;5079:72119:9;;38558:19;38549:28;;1796:162:0;1710:6;5079:72119:9;-1:-1:-1;;;;;5079:72119:9;735:10:7;1855:23:0;1851:101;;1796:162::o;1851:101::-;1901:40;;;1710:6;1901:40;735:10:7;1901:40:0;5079:72119:9;;1710:6:0;1901:40;2336:287:4;1759:1;2468:7;5079:72119:9;2468:19:4;1759:1;;;2468:7;1759:1;2336:287::o;1759:1::-;5079:72119:9;;-1:-1:-1;;;1759:1:4;;;;;;;;;;;5079:72119:9;1759:1:4;5079:72119:9;;;1759:1:4;;;;17917:482:9;-1:-1:-1;;;;;5079:72119:9;;;17977:20;;17973:46;;5079:72119;17995:1;5079:72119;18034:8;5079:72119;;;;17995:1;5079:72119;;;18030:46;;18179:18;;:23;18175:49;;5079:72119;17995:1;5079:72119;18034:8;5079:72119;;;17995:1;5079:72119;18256:4;5079:72119;;;;;;;;18271:7;5079:72119;;;;;;;;;;18256:4;5079:72119;;;18271:7;5079:72119;;:::i;:::-;18271:7;5079:72119;;17995:1;5079:72119;18302:18;5079:72119;;;17995:1;5079:72119;1759:1:4;18372:19:9;17995:1;18372:19;;17917:482::o;18175:49::-;18006:13;;;17995:1;18211:13;18034:8;17995:1;18211:13;18030:46;18059:17;;;17995:1;18059:17;18034:8;17995:1;18059:17;20710:200;-1:-1:-1;;;;;;;;;;;20710:200:9;;21973:113;;;;;20855:15;5079:72119;;20710:200::o;5079:72119::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;-1:-1:-1;5079:72119:9;;-1:-1:-1;5079:72119:9;;;;-1:-1:-1;5079:72119:9;;;;-1:-1:-1;5079:72119:9;;;;-1:-1:-1;5079:72119:9;;;;-1:-1:-1;5079:72119:9;;;;;;:::o;30066:328::-;30302:84;30066:328;;;;5079:72119;;:::i;:::-;30302:84;;;;;:::i;:::-;30278:7;;;30213:173;30266:10;;;5079:72119;-1:-1:-1;;;;;5079:72119:9;;;30254:10;;;5079:72119;;;;30243:9;;;5079:72119;-1:-1:-1;30233:8:9;;5079:72119;;;;-1:-1:-1;30224:7:9;;5079:72119;;;;;;;30066:328::o;19572:98::-;-1:-1:-1;;;;;;;;;;;19626:36:9;;;;;;5079:72119;;;;;;;;;;19626:36;;-1:-1:-1;;;;;;;;;;;19626:36:9;;;;;;;;19572:98;:::o;19626:36::-;;;;;:::i;:::-;19572:98::o;27819:335::-;5079:72119;;-1:-1:-1;;;27924:47:9;;27956:4;27924:47;;;5079:72119;-1:-1:-1;;;;;5079:72119:9;;;;;;;27924:47;5079:72119;;;-1:-1:-1;;;;;;;;;;;27924:47:9;;;;;;;-1:-1:-1;27924:47:9;;;27819:335;27986:12;;;27982:165;;27819:335;;:::o;27982:165::-;28117:17;28019:7;28015:51;;27982:165;-1:-1:-1;;;;;;;;;;;28117:17:9;:::i;28015:51::-;28028:38;;-1:-1:-1;;;;;;;;;;;28028:38:9;:::i;:::-;28015:51;;27924:47;;;;;;;;;;;;;;5079:72119;27924:47;;;:::i;:::-;;;5079:72119;;;;;27924:47;;;;;;;-1:-1:-1;27924:47:9;;27819:335;5079:72119;;-1:-1:-1;;;27924:47:9;;27956:4;27924:47;;;5079:72119;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;27924:47;5079:72119;;;;27924:47;;;;;;;-1:-1:-1;27924:47:9;;;27819:335;27986:12;;;27982:165;;27819:335;;;:::o;27982:165::-;28117:17;28019:7;28015:51;28117:17;28015:51;28028:38;;;;:::i;:::-;28117:17;:::i;27924:47::-;;;;;;;;;;;;;;5079:72119;27924:47;;;:::i;:::-;;;5079:72119;;;;;27924:47;;;;;;;-1:-1:-1;27924:47:9;;5079:72119;;;;;;;;;;:::o;30877:1283::-;;;;31166:13;;31162:27;;32145:6;31221:39;;;:18;;;:39;:18;;;:39;;;31398:12;5079:72119;-1:-1:-1;;;;;5079:72119:9;;;31762:16;;5079:72119;;;;;31394:337;;31221:39;31762:16;;;:::i;:::-;31845:19;;;;31841:44;;31221:39;31907:19;;;;:::i;:::-;32013:12;;;;32009:95;;31221:39;-1:-1:-1;;;;;;;5079:72119:9;32145:6;:::i;32009:95::-;32083:8;;-1:-1:-1;;;;;5079:72119:9;;32083:8;:::i;:::-;32009:95;;;;31841:44;31866:19;-1:-1:-1;31866:19:9;31841:44;;31394:337;5079:72119;;;;;31445:14;31441:111;;31394:337;-1:-1:-1;5079:72119:9;;;;;31570:18;;;;:41;;31394:337;31566:154;31394:337;31566:154;31696:6;5079:72119;;;;;;;31566:154;;;31394:337;;31570:41;31592:19;;;;31570:41;;31441:111;31528:6;5079:72119;;;;;-1:-1:-1;5079:72119:9;31441:111;;31221:39;;;;;;31162:27;31181:8;;;;;;5079:72119;31181:8;:::o;28617:390::-;5079:72119;;28862:9;;;;5079:72119;28886:9;;;;5079:72119;28910:10;;;;5079:72119;28935:9;;;;5079:72119;28959:10;;;;5079:72119;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;28770:229;;5079:72119;;28770:229;28617:390::o;21211:204::-;-1:-1:-1;;;;;;;;;;;21211:204:9;;21973:113;;;;;21358:16;5079:72119;;21211:204::o;5079:72119::-;;;-1:-1:-1;;;5079:72119:9;;;21337:1;5079:72119;;;;;;;;;;;;;;;;1618:188:6;5079:72119:9;;-1:-1:-1;;;1745:53:6;;;;-1:-1:-1;;;;;5079:72119:9;;;1745:53:6;;;5079:72119:9;;;;;;;;;;;;;;;;;1745:53:6;;;;;;5079:72119:9;;1745:53:6;:::i;:::-;;:::i;19870:340:9:-;5079:72119;;-1:-1:-1;;;19954:36:9;;19984:4;19954:36;;;5079:72119;19870:340;;-1:-1:-1;;5079:72119:9;;19954:36;5079:72119;-1:-1:-1;;;;;;;;;;;19954:36:9;;;;;;;;-1:-1:-1;19954:36:9;;;19870:340;19954:46;;5079:72119;;-1:-1:-1;;;;;;;;;;;20037:28:9;;;;5079:72119;;-1:-1:-1;;;20037:28:9;;19954:36;20037:28;;5079:72119;;;-1:-1:-1;5079:72119:9;19954:36;5079:72119;-1:-1:-1;;;;;;;;;;;;20037:28:9;;;;;;;;19870:340;20122:26;;;;;;;;;;:::i;:::-;;5079:72119;;;19870:340::o;5079:72119::-;;;-1:-1:-1;;;5079:72119:9;;;19954:36;5079:72119;;;;19954:36;5079:72119;;;;;;;;;;;20037:28;;;;;-1:-1:-1;20037:28:9;;:::i;:::-;-1:-1:-1;20037:28:9;;;;5079:72119;;;-1:-1:-1;;;5079:72119:9;;;19954:36;5079:72119;;;;19954:36;5079:72119;;;-1:-1:-1;;;5079:72119:9;;;;;;;19954:36;;;;5079:72119;19954:36;;5079:72119;19954:36;;;;;;5079:72119;19954:36;;;:::i;:::-;;;5079:72119;;;;;;;19954:36;;;;;;-1:-1:-1;19954:36:9;;32552:1178;;;;;32811:13;;32807:27;;32866:24;;;;;;:51;;;32996:12;5079:72119;-1:-1:-1;;;;;5079:72119:9;;;33360:16;;5079:72119;;;;;32992:337;;33360:16;;;:::i;:::-;33391:19;;;;33387:44;;32866:51;33453:19;;;;:::i;:::-;33489:12;;;;33485:146;;32866:51;33655:26;;5079:72119;33655:26;;;;;;;;;:::i;:::-;;5079:72119;;;32552:1178::o;5079:72119::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;33485:146;5079:72119;33532:38;;;;;;;;;;;;:::i;:::-;;5079:72119;;;33485:146;;;;;;5079:72119;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;33387:44;33412:19;-1:-1:-1;33412:19:9;33387:44;;32992:337;5079:72119;;;;;33043:14;33039:111;;32992:337;-1:-1:-1;5079:72119:9;;;;;33168:18;;;;:41;;32992:337;33164:154;32992:337;33164:154;33294:6;5079:72119;;;;;;;33164:154;;;32992:337;;33168:41;33190:19;;;;33168:41;;33039:111;33126:6;5079:72119;;;;;-1:-1:-1;5079:72119:9;33039:111;;32866:51;;;;;;32807:27;-1:-1:-1;5079:72119:9;;-1:-1:-1;;;32826:8:9:o;1219:160:6:-;5079:72119:9;;-1:-1:-1;;;1328:43:6;;;;-1:-1:-1;;;;;5079:72119:9;;;1328:43:6;;;5079:72119:9;;;;;;;;;1328:43:6;;;;;;5079:72119:9;;1328:43:6;:::i;5079:72119:9:-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;5079:72119:9;;-1:-1:-1;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;:::i;:::-;;;:::o;25231:1332::-;;;5079:72119;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;5079:72119:9;;;;;;;;;-1:-1:-1;5079:72119:9;;;;25358:26;5079:72119;;;;;;;;;;;;;;;-1:-1:-1;5079:72119:9;25475:8;5079:72119;;;;-1:-1:-1;5079:72119:9;;;25474:12;25470:31;;-1:-1:-1;5079:72119:9;;;25640:10;;;5079:72119;;;25665:10;;;5079:72119;;;;-1:-1:-1;;;25532:154:9;;;;;;-1:-1:-1;;;;;5079:72119:9;;;25532:154;;;5079:72119;;;;;;;;;;;;;25640:10;;5079:72119;25665:10;-1:-1:-1;;;;25532:154:9;;5079:72119;;;;25532:154;5079:72119;25532:154;25734:16;;;;;;;:::i;:::-;25765:8;;5079:72119;;25765:27;;25231:1332;25761:46;;5079:72119;;25936:55;;;5079:72119;25936:55;5079:72119;25936:55;;5079:72119;;;;;;25936:55;;5079:72119;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;:::i;:::-;;;;26072:8;;;;:34;;;25231:1332;26068:234;;25231:1332;26326:8;;;;;;;;:34;;;25231:1332;26322:234;;25231:1332;;;;;;;;:::o;26322:234::-;26440:104;26377:20;26440:104;26377:20;;5079:72119;26377:20;;5079:72119;;;;;;;;;;;;;;;;;;;;26518:11;;5079:72119;;;26440:104;;;5079:72119;26440:104;;;:::i;:::-;5079:72119;26419:18;;:125;26322:234;;;;;;;;;26326:34;26338:11;;;5079:72119;26338:22;;26326:34;;26068:234;26186:104;26123:20;;5079:72119;26186:104;26123:20;;5079:72119;;;;;;;;;;;;;;;;;;;;26264:11;;5079:72119;;;26186:104;;;5079:72119;26186:104;;;:::i;:::-;5079:72119;26165:18;;:125;26068:234;;;;;;;26072:34;26084:11;;;5079:72119;26084:22;;26072:34;;25761:46;25794:13;;;;;;:::o;25765:27::-;5079:72119;;;25777:15;25765:27;;25470:31;25488:13;-1:-1:-1;;;;25488:13:9:o;22639:845::-;22795:18;;;5079:72119;22795:18;22816:19;;:29;5079:72119;22795:50;;22791:317;22795:18;;;22891:19;;;22795:18;22866:22;;:44;22929:30;22639:845::o;22791:317::-;23006:22;22795:18;23006:22;;22795:18;23006:22;;:32;5079:72119;-1:-1:-1;22981:127:9;;22791:317;;22639:845::o;22981:127::-;23059:33;22639:845::o;:::-;23153:18;;;5079:72119;23174:19;;;;;23153:18;23174:19;;:29;5079:72119;23153:50;;23149:317;23153:18;;;23249:19;23224:22;23249:19;;23224:22;;:44;23287:30;22639:845::o;23149:317::-;23364:22;;;;;23153:18;23364:22;;:32;5079:72119;-1:-1:-1;23339:127:9;;23149:317;;22639:845::o;24052:453::-;;;;;5079:72119;;:::i;:::-;;;;:::i;:::-;;24274:18;;;5079:72119;24274:18;24295:14;;5079:72119;24274:35;;24270:198;24274:18;;;24326:11;;24052:453;:::o;24270:198::-;24274:18;24410:14;;;;5079:72119;-1:-1:-1;24385:83:9;;24270:198;24478:19;24052:453;:::o;24385:83::-;24441:15;-1:-1:-1;24478:19:9;24052:453;:::o;5079:72119::-;;;-1:-1:-1;;5079:72119:9;;;;;;;:::o;57766:3470::-;58096:28;58157;57766:3470;;;;58096:28;:::i;:::-;58157;;:::i;:::-;58395:57;58206:10;;;5079:72119;;58206:14;;:32;;;57766:3470;58198:61;;;:::i;:::-;58278:9;;;;5079:72119;58291:9;;;5079:72119;58270:50;;-1:-1:-1;;;;;5079:72119:9;;;;;58278:22;58270:50;:::i;:::-;58339:10;;;;5079:72119;58353:10;;;5079:72119;58331:53;;-1:-1:-1;;;;;5079:72119:9;;;;;58339:24;58331:53;:::i;:::-;5079:72119;58206:10;58417;;5079:72119;58403:24;58395:57;:::i;:::-;5079:72119;;;;58719:7;;;;;;58737;;;;;;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;58737:7;;58719;5079:72119;;;;58623:2;;5079:72119;;58737:7;5079:72119;;;;;:::i;:::-;;;;;;;;;;;;27095:71;;5079:72119;27095:71;;;5079:72119;;;;;;:::i;:::-;58623:2;5079:72119;;;;27095:71;;;;;;;;;;-1:-1:-1;27095:71:9;;;57766:3470;5079:72119;27252:71;5079:72119;;;;;;;;;:::i;:::-;;;;;-1:-1:-1;;;27252:71:9;;5079:72119;;;;;27095:71;27252;;;:::i;:::-;;;;;;;;;;;-1:-1:-1;27252:71:9;;;57766:3470;27351:11;;;;:::i;:::-;58782:42;58623:2;59009;59028:13;5079:72119;;;59024:2;59013:13;;;;58623:2;59062:13;59058:27;;59122:95;59168:7;;;59186;;;59122:95;;;:::i;:::-;59238:24;;;59234:136;;59028:13;;5079:72119;59009:2;59028:13;58992:19;5079:72119;;;;;;58992:19;5079:72119;;;;-1:-1:-1;5079:72119:9;;27095:71;5079:72119;;-1:-1:-1;5079:72119:9;59234:136;59283:24;-1:-1:-1;59283:24:9;-1:-1:-1;59283:24:9;5079:72119;59234:136;;59058:27;5079:72119;59009:2;59077:8;;;59013:13;-1:-1:-1;;5079:72119:9;;59013:13;;;;;;5079:72119;;;;:::i;:::-;59632:1;5079:72119;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;;;;;;;;-1:-1:-1;59776:13:9;5079:72119;;59791:18;;;;;5079:72119;;;;;;;;;;;;;;;;;;-1:-1:-1;60091:10:9;;;;:49;;;59776:13;60090:129;;;;59776:13;60068:248;;60378:189;;;60454:7;;;;:::i;:::-;5079:72119;;;;;;;;;;;60378:189;5079:72119;;;;60682:17;;;:39;;;;60378:189;60678:381;;;60378:189;5079:72119;;;59776:13;;;;;60678:381;60762:108;60812:7;;;;;60830;;60762:108;;:::i;:::-;60895:24;;;60891:153;;60678:381;;;;60891:153;60944:24;-1:-1:-1;60944:24:9;-1:-1:-1;5079:72119:9;60891:153;;;60682:39;60719:2;60703:18;;;;60682:39;;;60378:189;60518:33;5079:72119;;;;;;;;;;60378:189;5079:72119;;;;-1:-1:-1;5079:72119:9;;27095:71;5079:72119;;-1:-1:-1;5079:72119:9;60068:248;5079:72119;;;;;60292:8;;;60090:129;60164:10;-1:-1:-1;60164:10:9;;:54;;60090:129;60164:54;5079:72119;;60198:20;5079:72119;;;60198:20;:::i;:::-;5079:72119;;;;60178:40;;60090:129;;60091:49;60132:7;5079:72119;60132:7;;;:::i;:::-;5079:72119;;;;60105:35;;60091:49;;59791:18;;;;;;;;;;61195:33;57766:3470;:::o;27252:71::-;;;5079:72119;27252:71;;5079:72119;27252:71;;;;;;5079:72119;27252:71;;;:::i;:::-;;;5079:72119;;;;27351:11;5079:72119;;27252:71;;;;;;-1:-1:-1;27252:71:9;;27095;;;;5079:72119;27095:71;;5079:72119;27095:71;;;;;;5079:72119;27095:71;;;:::i;:::-;;;5079:72119;;;;;;27252:71;27095;;;;;-1:-1:-1;27095:71:9;;58206:32;-1:-1:-1;58206:10:9;58224;;5079:72119;58224:14;;58206:32;;5079:72119;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;36730:800::-;36900:12;5079:72119;-1:-1:-1;;;;;5079:72119:9;;36927:17;;36923:36;;5079:72119;;-1:-1:-1;;;36976:47:9;;5412:42;36976:47;;;5079:72119;5490:42;5079:72119;;;;36976:47;5079:72119;36976:47;5079:72119;36976:47;;;;;-1:-1:-1;;;36976:47:9;;;36730:800;-1:-1:-1;36972:551:9;;37498:13;;;;-1:-1:-1;37498:13:9;-1:-1:-1;37498:13:9;:::o;36972:551::-;-1:-1:-1;37129:11:9;;;37125:32;;5079:72119;36976:47;5079:72119;;;;;;;;;;37184:40;;5412:42;36976:47;37184:40;;5079:72119;5490:42;5079:72119;;;;37184:40;;;;;;;-1:-1:-1;37184:40:9;;;36972:551;-1:-1:-1;5079:72119:9;;;37291:2;37285:8;;37291:2;;;37318:8;;37311:16;37318:8;37307:20;37318:8;;:::i;:::-;37311:16;:::i;:::-;37307:20;;:::i;:::-;37441:23;;:::o;37281:145::-;37291:2;-1:-1:-1;37291:2:9;;;37384:8;37377:16;37384:8;37373:20;37384:8;;:::i;37377:16::-;37373:20;;:::i;37347:79::-;37413:13;37441:23;;:::o;37184:40::-;;;5079:72119;37184:40;;5079:72119;37184:40;;;;;;5079:72119;37184:40;;;:::i;:::-;;;5079:72119;;;;;;;;;;;;37184:40;;;;;;-1:-1:-1;37184:40:9;;37125:32;37142:15;;-1:-1:-1;37142:15:9;;:::o;36976:47::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;5079:72119;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;36976:47;;;;;;;;-1:-1:-1;36976:47:9;;36923:36;-1:-1:-1;;;;;36946:13:9:o;36730:800::-;36900:12;5079:72119;-1:-1:-1;;;;;5079:72119:9;;36730:800;36927:17;;36923:36;;5079:72119;;-1:-1:-1;;;36976:47:9;;-1:-1:-1;;;;;5079:72119:9;;;36976:47;;;5079:72119;;;5490:42;5079:72119;;;;;;;36976:47;5079:72119;36976:47;;;;;-1:-1:-1;;;36976:47:9;;;36730:800;-1:-1:-1;36972:551:9;;37498:13;;;;;-1:-1:-1;37498:13:9;-1:-1:-1;37498:13:9;:::o;36972:551::-;-1:-1:-1;37129:11:9;;;37125:32;;5079:72119;;36976:47;5079:72119;;;;;;;;;37184:40;;36976:47;37184:40;;5079:72119;5490:42;5079:72119;;;;37184:40;;;;;;;-1:-1:-1;37184:40:9;;;-1:-1:-1;5079:72119:9;;;37291:2;37285:8;;37291:2;;;37318:8;;37311:16;37318:8;37307:20;37318:8;;:::i;37125:32::-;37142:15;;;-1:-1:-1;37142:15:9;;:::o;36976:47::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;5079:72119;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;36976:47;;;;;;;;-1:-1:-1;36976:47:9;;36923:36;-1:-1:-1;;;;;;36946:13:9:o;36730:800::-;36900:12;5079:72119;-1:-1:-1;;;;;5079:72119:9;;36730:800;36927:17;;36923:36;;5079:72119;;-1:-1:-1;;;36976:47:9;;-1:-1:-1;;;;;5079:72119:9;;;36976:47;;;5079:72119;;;5412:42;5079:72119;;;;;;;36976:47;5079:72119;36976:47;;;;;-1:-1:-1;;;36976:47:9;;;36730:800;-1:-1:-1;36972:551:9;;37498:13;;;;;-1:-1:-1;37498:13:9;-1:-1:-1;37498:13:9;:::o;36972:551::-;-1:-1:-1;37129:11:9;;;37125:32;;5079:72119;;36976:47;5079:72119;;;;;;;;;37184:40;;36976:47;37184:40;;5079:72119;5412:42;5079:72119;;;;37184:40;;;;;;;-1:-1:-1;37184:40:9;;;-1:-1:-1;5079:72119:9;;;37291:2;37285:8;;37291:2;;;37318:8;;37311:16;37318:8;37307:20;37318:8;;:::i;36976:47::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;5079:72119;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;36976:47;;;;;;;;-1:-1:-1;36976:47:9;;5084:380:6;5079:72119:9;;5199:47:6;;-1:-1:-1;5199:47:6;;;5079:72119:9;;;;5199:47:6;;5079:72119:9;;;;;;5199:47:6;;;;;5079:72119:9;;;;;;5199:47:6;;;;;;;:::i;:::-;9770:199;;;;;;;-1:-1:-1;9770:199:6;;9985:80;;;5084:380;5261:45;;;5257:201;;5084:380;;;:::o;5257:201::-;5349:43;5434:12;5079:72119:9;;;;;;;5199:47:6;5349:43;;;5199:47;5349:43;;5079:72119:9;-1:-1:-1;5199:47:6;5079:72119:9;;;5199:47:6;5349:43;;;5199:47;5349:43;;:::i;:::-;;;:::i;9985:80::-;9997:67;;-1:-1:-1;9997:15:6;;-1:-1:-1;;;;;;5079:72119:9;;10015:26:6;:30;;9997:67;9985:80;;;;9997:67;10063:1;10048:16;9997:67;;5084:380;5079:72119:9;;5199:47:6;;-1:-1:-1;5199:47:6;;;5079:72119:9;;;;5199:47:6;;5079:72119:9;;;;;;5199:47:6;;;;;5079:72119:9;;;;;;;5199:47:6;;;;;;;:::i;8370:720::-;;-1:-1:-1;8507:421:6;8370:720;8507:421;;;;;;;;;;;;-1:-1:-1;8507:421:6;;8942:15;;-1:-1:-1;;;;;;5079:72119:9;;8960:26:6;:31;8942:68;8938:146;;8370:720;:::o;8938:146::-;-1:-1:-1;;;;9033:40:6;;;-1:-1:-1;;;;;5079:72119:9;;;;9033:40:6;5079:72119:9;;;9033:40:6;8942:68;9009:1;8994:16;;8942:68;;5079:72119:9;;;-1:-1:-1;;;;;5079:72119:9;;;;;;:::o;29414:318::-;;5079:72119;;:::i;:::-;;;;29640:84;;5079:72119;;;;;;;;;29640:84;;;5079:72119;:::i;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;29640:84;;;;;5079:72119;;;;:::i;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::o;26739:631::-;27095:71;26739:631;;;;5079:72119;26739:631;26995:14;;;:::i;:::-;5079:72119;;;;;;;:::i;:::-;;;;;;;;;;;;;;27095:71;;;;;;:::i;:::-;;;-1:-1:-1;;;;;5079:72119:9;27095:71;;;;;;;-1:-1:-1;27095:71:9;;;26739:631;5079:72119;;27252:71;5079:72119;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;27252:71;;27095;27252;;;:::i;:::-;;;-1:-1:-1;;;;;5079:72119:9;27252:71;;;;;;;-1:-1:-1;27252:71:9;;;26739:631;27351:11;;;;:::i;27252:71::-;;;5079:72119;27252:71;;5079:72119;27252:71;;;;;;5079:72119;27252:71;;;:::i;:::-;;;5079:72119;;;;27351:11;5079:72119;;27252:71;;;;;;-1:-1:-1;27252:71:9;;27095;;;;;5079:72119;27095:71;;5079:72119;27095:71;;;;;;5079:72119;27095:71;;;:::i;:::-;;;5079:72119;;;;;;;27252:71;27095;;;;;-1:-1:-1;27095:71:9;
Swarm Source
ipfs://ae4883b2f6d001d10dab0eb4cafb86cfab7dc8d9d456146282072e794eb0d275
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.