// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { IAllowanceTransfer } from "permit2/src/interfaces/IAllowanceTransfer.sol";
import {
ISwapRouter02,
ISwapRouterV1,
IUniswapV3Factory,
IUniswapV3Pool
} from "../interfaces/IUniswap.sol";
import { OracleTwap } from "../libraries/OracleTwap.sol";
/// @title NasdankOrderRouterV2 — ANY-token → ANY-token Ultra order executor (Jupiter-style).
/// @author NASDANK web3-dev (Ultra Phase 3)
/// @notice A careful EXTENSION of {NasdankOrderRouter} (the live v1). Everything about v1 is preserved:
/// the {Order} struct, the EIP-712 {ORDER_TYPEHASH}, the domain name/version, the two-signature
/// authorisation, every fail-closed bound, the permissionless crank, the zero-residual
/// post-condition, and — crucially — the audited FEE MODEL: **EXACTLY 1% (100 bps), ALWAYS taken
/// in WETH, ALWAYS to the immutable {TREASURY}**.
///
/// WHAT'S NEW: v1 HARD-REQUIRED exactly one leg to be WETH (`NotWethPair`), because the fee is
/// always a WETH fee and the route was a single `exactInputSingle`. v2 adds a third route so a
/// user can trade **meme → meme** (e.g. PENGU → LAMBO) on all four order types:
///
/// ┌────────┬──────────────────────────┬───────────────────────────────────────────────────┐
/// │ route │ shape │ execution │
/// ├────────┼──────────────────────────┼───────────────────────────────────────────────────┤
/// │ BUY │ tokenIn == WETH9 │ fee = 1% of amountIn (BEFORE swap), then │
/// │ │ │ exactInputSingle(WETH → tokenOut) │
/// │ SELL │ tokenOut == WETH9 │ exactInputSingle(tokenIn → WETH), then │
/// │ │ │ fee = 1% of the WETH output (AFTER swap) │
/// │ CROSS │ NEITHER leg is WETH9 │ hop1 exactInputSingle(tokenIn → WETH), │
/// │ (new) │ (tokenIn != tokenOut) │ fee = 1% of the RECEIVED WETH, │
/// │ │ │ hop2 exactInputSingle(WETH → tokenOut) with the │
/// │ │ │ REMAINING 99% of that WETH │
/// └────────┴──────────────────────────┴───────────────────────────────────────────────────┘
///
/// BUY and SELL are byte-for-byte the v1 logic (no regression). CROSS is TWO separate
/// `exactInputSingle` calls on the immutable {POOL_FEE} tier — deliberately NOT a path-encoded
/// `exactInput` — so the router physically holds the intermediate WETH for the instant between
/// hops and can take the fee in WETH from it, preserving the audited invariant:
///
/// *** THE FEE IS ALWAYS EXACTLY 1% OF A WETH LEG, IN WETH, TO THE IMMUTABLE TREASURY. ***
///
/// WHERE THE ROUNDING DUST GOES (it must ALWAYS favour the USER, never the treasury):
/// - BUY: fee = floor(amountIn * 100 / 10000). The truncated remainder stays in
/// `amountIn - fee`, which is swapped FOR THE USER. User-favouring.
/// - SELL: fee = floor(amountOut * 100 / 10000). The remainder is swept to the owner.
/// User-favouring.
/// - CROSS: fee = floor(wethMid * 100 / 10000) where `wethMid` is the ACTUAL WETH returned by
/// hop 1. The remainder `wethMid - fee` is fed ENTIRELY into hop 2 → the user's
/// tokenOut. User-favouring. The intermediate WETH is therefore fully consumed
/// (fee + hop2In == wethMid, exactly), leaving ZERO WETH in the router.
/// A fill so small that `floor(...) == 0` pays no fee at all — again user-favouring, and
/// identical to v1's behaviour on a dust buy.
///
/// SLIPPAGE FLOOR (MARKET/DCA, `maxSlippageBps != 0`) — TWAP, fail-closed, UNWEAKENED, and
/// **POOL-FEE-COMPENSATED ON-CHAIN**. A Uniswap TWAP is a MID price: it does NOT include the fee
/// the pool charges on the swap. A BUY/SELL crosses ONE pool (pays one {POOL_FEE}); a CROSS
/// crosses TWO (pays it TWICE). If the floor were built on the raw mid, `maxSlippageBps` would
/// have to silently absorb those pool fees — a CROSS would need ≥ ~199 bps just to break even at
/// ZERO price impact, so the UI's 150 bps minimum could NEVER fill and its 200 bps preset would
/// die on any real impact (a meme→meme DCA stuck at 0% forever). The CONTRACT therefore discounts
/// EACH leg's TWAP by that leg's pool fee, so `maxSlippageBps` means what its name says (tolerance
/// to PRICE IMPACT + drift), and the frontend needs no compensation math:
/// - BUY/SELL: twapOut = twap(in→out, amountIn); twapOut -= twapOut * POOL_FEE / 1e6
/// - CROSS: wethMid = twap(tokenIn→WETH, amountIn); wethMid -= wethMid * POOL_FEE / 1e6
/// afterFee = wethMid - 1% Ultra fee
/// twapOut = twap(WETH→tokenOut, afterFee); twapOut -= twapOut * POOL_FEE / 1e6
/// - then in every route: floor = twapOut * (BPS - maxSlippageBps) / BPS
/// BOTH pools must INDEPENDENTLY pass the same oracle-quality gate as v1 (cardinality ≥
/// {MIN_ORACLE_CARDINALITY}, newest observation ≤ `TWAP_WINDOW / MAX_EXTRAP_DIVISOR` old,
/// `observe` in try/catch). Either one failing ⇒ `OracleNotReady` (fail closed; there is no
/// slot0 price read anywhere in this contract). A CROSS order is thus only fillable when BOTH
/// legs have a genuine, recently-anchored TWAP.
/// - LIMIT/COUNTER never read the oracle in ANY route: their floor is the user-signed
/// `limitPriceX96` (tokenOut-per-tokenIn, Q96), so they remain the safe choice on thin pools.
/// `limitPriceX96` means ONE thing in ALL THREE routes: the user is guaranteed at least
/// `0.99 * limitPriceX96` tokenOut per GROSS tokenIn spent (the 1% Ultra fee is netted off the
/// basis on CROSS, exactly as it is netted off the swap input on BUY and off the output on
/// SELL). v1's frontend limit math therefore keeps working unchanged on every route.
///
/// THE FLOOR ALWAYS BINDS THE **FINAL** tokenOut, NEVER THE INTERMEDIATE WETH. Hop 1 is executed
/// with `amountOutMinimum = 0` ON PURPOSE: an intermediate floor would be a second, independently
/// gameable constraint (and a griefing/DoS knob) while adding no protection — if hop 1 is
/// sandwiched, less WETH reaches hop 2, hop 2 delivers less tokenOut, and the FINAL floor rejects
/// the fill. One binding constraint, on the only quantity the user actually receives.
///
/// ⚠️ HONEST MEV NOTE — TWO POOLS = TWO SANDWICH SURFACES. A CROSS fill touches two pools in one
/// transaction, so an adversary can sandwich either or both hops. The user's protection is the
/// SINGLE end-to-end floor on the final tokenOut (TWAP-derived for MARKET/DCA, signed price for
/// LIMIT/COUNTER): any sandwich deep enough to matter pushes the final output below that floor and
/// the whole fill REVERTS atomically (nothing is left stranded — the intermediate WETH only exists
/// inside the transaction). What a CROSS fill DOES concede versus a single-hop fill is that the
/// attacker's *profitable* band is the COMPOSITION of the two pools' tolerances: the user's signed
/// `maxSlippageBps` is now spent across two pools' price impact + two extrapolation tails instead
/// of one, so the same bps buys less protection per pool. Users trading illiquid meme→meme pairs
/// should size fills small (DCA) and/or use LIMIT/COUNTER. This is inherent to any two-hop route
/// (Uniswap's own `exactInput` multi-hop has the identical surface) and is NOT a custody risk: the
/// router still cannot deliver output to anyone but `order.owner`, and still keeps nothing.
///
/// NO CUSTODY, NO ADMIN: no owner, no upgrade path, no `delegatecall`, no arbitrary call, no
/// `selfdestruct`, no `receive()`, no function that pays out anything the contract holds — because
/// it is designed to hold NOTHING, asserted by a zero-residual post-condition on tokenIn, tokenOut,
/// **WETH** (new: the CROSS intermediate) and native ETH after EVERY fill.
contract NasdankOrderRouterV2 is ReentrancyGuard {
using SafeERC20 for IERC20;
using SafeCast for uint256;
// --------------------------------------------------------------------- types
/// @notice Order kind. The value is signed as part of the Order and validated on-chain.
/// @dev MARKET: one-shot immediate swap, protected by `maxSlippageBps`. LIMIT: fills only when
/// the pool meets `limitPriceX96`. DCA: recurring buys/sells gated by `minIntervalSec`,
/// protected by `maxSlippageBps`. COUNTER: opposite-side price-bounded order (like LIMIT).
enum OrderType {
MARKET,
LIMIT,
DCA,
COUNTER
}
/// @notice The execution route, derived ON-CHAIN from (tokenIn, tokenOut) vs {WETH9}. NOT signed and
/// NOT caller-supplied — it is a pure function of the order's legs, so a caller cannot pick a
/// cheaper/looser route than the user's tokens imply.
/// @dev BUY: tokenIn == WETH9. SELL: tokenOut == WETH9. CROSS: neither leg is WETH9 (two hops).
enum Route {
BUY,
SELL,
CROSS
}
/// @notice The trade terms the user signs off-chain (EIP-712). The router hashes this, recovers the
/// signer, and requires it == {owner}. Every field is a binding on-chain constraint.
/// @dev BYTE-FOR-BYTE IDENTICAL to {NasdankOrderRouter}'s (v1) struct, and hashed with the IDENTICAL
/// {ORDER_TYPEHASH} under the IDENTICAL EIP-712 domain name/version — so the frontend and the
/// keeper's signing code are unchanged. (The domain's `verifyingContract` is of course this
/// contract's address, which correctly prevents replaying a v1 order on v2 and vice-versa.)
/// @param owner The order owner: signer, sole funding source, and sole output recipient.
/// @param tokenIn Input asset pulled via Permit2 (WETH, or ANY token — see {Route}).
/// @param tokenOut Output asset delivered to {owner} (WETH, or ANY token — see {Route}).
/// @param orderType {OrderType} as a uint8.
/// @param maxAmountPerFill Hard upper bound on a single fill's `fillAmount` (in `tokenIn`).
/// @param minIntervalSec Minimum seconds between consecutive fills (schedule gate for DCA).
/// @param startTime Earliest fill timestamp (inclusive).
/// @param endTime Latest fill timestamp (inclusive); also the Permit2 expiration.
/// @param totalBudget Cumulative cap across all fills (in `tokenIn`); == the Permit2 amount.
/// @param limitPriceX96 Minimum acceptable `tokenOut`-per-`tokenIn` ratio, Q64.96. 0 = no bound.
/// @param maxSlippageBps Max slippage vs. the TWAP for MARKET/DCA, in bps of the derived floor.
/// @param salt Uniqueness nonce so identical terms produce distinct orders.
struct Order {
address owner;
address tokenIn;
address tokenOut;
uint8 orderType;
uint256 maxAmountPerFill;
uint256 minIntervalSec;
uint256 startTime;
uint256 endTime;
uint256 totalBudget;
uint256 limitPriceX96;
uint256 maxSlippageBps;
uint256 salt;
}
// --------------------------------------------------------------------- constants
/// @notice Protocol fee in basis points — EXACTLY 1%, immutable by construction (a `constant`).
uint256 public constant FEE_BPS = 100;
/// @notice Basis-point denominator.
uint256 public constant BPS_DENOM = 10_000;
/// @notice Sanity cap on `maxSlippageBps` (50%). A looser bound would defeat slippage protection.
uint256 public constant MAX_SLIPPAGE_BPS = 5_000;
/// @notice Q64.96 fixed-point unit (2**96), used for `limitPriceX96` and TWAP price math.
uint256 internal constant Q96 = 0x1000000000000000000000000;
/// @notice Denominator of a Uniswap V3 fee tier ({POOL_FEE} is in hundredths of a bip ⇒ 1e6 = 100%).
/// Used to discount each TWAP leg (a MID price) by the pool fee that leg will actually pay.
uint256 internal constant POOL_FEE_DENOM = 1_000_000;
/// @notice Bounds on the constructor `twapWindow`. A too-short window is single-block-manipulable;
/// a too-long one makes the floor stale.
uint32 public constant MIN_TWAP_WINDOW = 60;
uint32 public constant MAX_TWAP_WINDOW = 3_600;
/// @notice A Uniswap TWAP is only a TRUE time-weighted average up to its newest stored observation;
/// beyond that, `observe` extrapolates the (manipulable) current tick. To keep that
/// extrapolated tail small we require the newest observation to be no older than
/// `TWAP_WINDOW / MAX_EXTRAP_DIVISOR`. DIV=4 ⇒ ≤25% extrapolation. UNCHANGED from v1.
uint256 internal constant MAX_EXTRAP_DIVISOR = 4;
/// @notice Minimum pool oracle cardinality to trust for a TWAP. UNCHANGED from v1.
uint16 internal constant MIN_ORACLE_CARDINALITY = 2;
/// @notice EIP-712 typehash for {Order}. BYTE-FOR-BYTE IDENTICAL to v1's — the signing payload the
/// frontend/keeper produce is unchanged.
bytes32 public constant ORDER_TYPEHASH = keccak256(
"Order(address owner,address tokenIn,address tokenOut,uint8 orderType,uint256 maxAmountPerFill,uint256 minIntervalSec,uint256 startTime,uint256 endTime,uint256 totalBudget,uint256 limitPriceX96,uint256 maxSlippageBps,uint256 salt)"
);
/// @dev EIP-712 domain type hash + hashed name/version. Inlined (rather than inheriting OZ's
/// `EIP712`) so the router builds under the project's `paris` target (OZ's EIP712 pulls in
/// `Strings -> Bytes.sol`, which uses the Cancun-only `mcopy`). The domain NAME and VERSION are
/// deliberately the SAME strings as v1 ("NasdankOrderRouter" / "1") so the frontend's typed-data
/// domain only changes its `verifyingContract` address.
bytes32 private constant _DOMAIN_TYPEHASH = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
bytes32 private constant _HASHED_NAME = keccak256("NasdankOrderRouter");
bytes32 private constant _HASHED_VERSION = keccak256("1");
bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
uint256 private immutable _CACHED_CHAIN_ID;
address private immutable _CACHED_THIS;
// --------------------------------------------------------------------- immutables
/// @notice Canonical Permit2 (AllowanceTransfer) — the ONLY funding rail. Non-re-pointable.
IAllowanceTransfer public immutable PERMIT2;
/// @notice Uniswap V3 swap router. Non-re-pointable. Calling convention selected by
/// {ROUTER_HAS_DEADLINE}: SwapRouter02 (no deadline) on mainnet 4663, or the v1 SwapRouter
/// (with deadline) on chains that only ship v1 (e.g. testnet 46630 — see {_swap}).
ISwapRouter02 public immutable SWAP_ROUTER;
/// @notice Whether {SWAP_ROUTER} uses the v1 `exactInputSingle` ABI (which includes a `deadline`).
/// Both ABIs enforce `amountOutMinimum`, so the slippage floor is identical.
bool public immutable ROUTER_HAS_DEADLINE;
/// @notice WETH9 — the fee leg of EVERY order, in EVERY route (the CROSS intermediate). Non-re-pointable.
address public immutable WETH9;
/// @notice Uniswap V3 factory, used to locate the canonical pools for on-chain TWAP reads.
IUniswapV3Factory public immutable FACTORY;
/// @notice Immutable Ultra fee sink. There is NO setter; the fee recipient can never be re-pointed.
address public immutable TREASURY;
/// @notice Pool fee tier (in hundredths of a bip) that BOTH hops route through. Immutable and NOT
/// caller-supplied, so a caller cannot route a fill through a manipulable low-liquidity pool.
uint24 public immutable POOL_FEE;
/// @notice TWAP window (seconds) for the MARKET/DCA slippage floor. Immutable; bounded
/// [MIN_TWAP_WINDOW, MAX_TWAP_WINDOW] at construction. LIMIT/COUNTER never use it.
uint32 public immutable TWAP_WINDOW;
// --------------------------------------------------------------------- storage
/// @notice Cumulative `tokenIn` filled per order hash. Enforces `totalBudget` independent of Permit2.
mapping(bytes32 => uint256) public cumulativeFilled;
/// @notice Timestamp of the most recent fill per order hash. Enforces `minIntervalSec`.
mapping(bytes32 => uint256) public lastFillAt;
/// @notice Whether an order hash has been cancelled by its owner. Cancelled orders never fill again.
mapping(bytes32 => bool) public cancelled;
// --------------------------------------------------------------------- events
/// @notice Emitted on every successful fill. Same event SIGNATURE (and therefore topic0) as v1.
/// @dev ⚠️ SEMANTIC CHANGE vs v1, DELIBERATE — READ BEFORE INDEXING. `amountOut` is now, in ALL
/// THREE ROUTES, the **NET amount of `tokenOut` actually delivered to `owner`**. v1 emitted the
/// GROSS pool output on a SELL (so `owner` really received `amountOut - feeWeth`) while emitting
/// the net figure on a BUY — an indexer computing "user received" uniformly was therefore wrong
/// by 1% on exactly one route. v2 makes the field mean ONE thing everywhere:
/// amountOut == what the owner received (BUY / SELL / CROSS alike).
/// For a BUY/CROSS into a fee-on-transfer `tokenOut`, this is the owner's REALISED (post-tax)
/// balance delta on delivery — NOT the pool's pre-tax output — so the figure is exactly what
/// the owner truly banked and never over-reports. `amountIn` is the full `fillAmount` pulled
/// from the owner (fee inclusive). `feeWeth` is
/// ALWAYS the WETH fee, in every route. The `execute` RETURN VALUE matches this field exactly.
/// Indexers: `effectivePrice = amountOut / amountIn`; do NOT subtract `feeWeth` from
/// `amountOut` on a SELL (it is already net). A CROSS fill additionally emits
/// {CrossRouteFilled} with the intermediate WETH leg.
event OrderFilled(
bytes32 indexed orderHash,
address indexed owner,
address indexed tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOut,
uint256 feeWeth,
uint256 cumulativeFilled
);
/// @notice Emitted (in addition to {OrderFilled}) ONLY on a CROSS (token→token) fill, exposing the
/// intermediate WETH leg so the indexer can verify `feeWeth == wethMid * 1%` off-chain.
/// @param orderHash The order's EIP-712 hash.
/// @param owner The order owner (sole recipient of `amountOut`).
/// @param tokenIn The input token (hop 1 in).
/// @param tokenOut The output token (hop 2 out).
/// @param wethMid WETH received from hop 1 — the fee base.
/// @param feeWeth Exactly 1% of `wethMid`, floored, sent to {TREASURY}.
/// @param hop2In `wethMid - feeWeth` — the WETH fed into hop 2 (the intermediate is fully consumed).
/// @param amountOut Final `tokenOut` delivered to `owner`.
event CrossRouteFilled(
bytes32 indexed orderHash,
address indexed owner,
address indexed tokenIn,
address tokenOut,
uint256 wethMid,
uint256 feeWeth,
uint256 hop2In,
uint256 amountOut
);
/// @notice Emitted when the 1% WETH fee is routed to the immutable treasury.
event UltraFeeCollected(bytes32 indexed orderHash, address indexed owner, uint256 feeWeth);
/// @notice Emitted when an owner cancels an order; all future fills of that hash revert.
event OrderCancelled(bytes32 indexed orderHash, address indexed owner);
// --------------------------------------------------------------------- errors
error ZeroAddress();
error InvalidOrderType();
error IdenticalTokens();
error BadWindow();
error ZeroAmount();
error ZeroIntermediate();
error FillTooSmall();
error InsufficientOutput();
error BudgetTooLarge();
error SlippageTooHigh();
error PriceBoundRequired();
error IntervalRequired();
error BadOrderSignature();
error PermitTokenMismatch();
error PermitSpenderMismatch();
error PermitAmountMismatch();
error PermitExpirationMismatch();
error OrderCancelledError();
error NotStarted();
error Expired();
error IntervalNotElapsed();
error FillTooLarge();
error BudgetExceeded();
error PoolNotFound();
error OracleNotReady();
error InvalidTwapWindow();
error NoOnChainFloor();
error NotOwner();
error TokenInResidual();
error TokenOutResidual();
error WethResidual();
error EthResidual();
error InvalidFeeTier();
// --------------------------------------------------------------------- constructor
/// @param permit2 Canonical Permit2 (AllowanceTransfer).
/// @param swapRouter Uniswap V3 swap router — SwapRouter02 (mainnet) or v1 SwapRouter (testnet).
/// @param weth9 WETH9 — the fee leg / CROSS intermediate.
/// @param factory Uniswap V3 factory.
/// @param treasury Immutable Ultra fee sink.
/// @param poolFee Fee tier BOTH hops route through (10000 for NASDANK's 1% pools).
/// @param routerHasDeadline `true` iff `swapRouter` uses the v1 `exactInputSingle` ABI (with a
/// `deadline`); `false` for SwapRouter02.
/// @param twapWindow TWAP window (seconds) for the MARKET/DCA slippage floor; must be within
/// [MIN_TWAP_WINDOW, MAX_TWAP_WINDOW].
/// @dev The §6 confirmed addresses are re-asserted against these args by the deploy script and the
/// fork tests. Zero addresses and a disabled fee tier are rejected so a misconfigured deploy
/// cannot produce a live-but-broken router.
constructor(
IAllowanceTransfer permit2,
ISwapRouter02 swapRouter,
address weth9,
IUniswapV3Factory factory,
address treasury,
uint24 poolFee,
bool routerHasDeadline,
uint32 twapWindow
) {
if (
address(permit2) == address(0) || address(swapRouter) == address(0)
|| weth9 == address(0) || address(factory) == address(0) || treasury == address(0)
) {
revert ZeroAddress();
}
// The routed pool tier must be an enabled Uniswap tier, else `getPool`/swaps silently break.
if (factory.feeAmountTickSpacing(poolFee) == 0) revert InvalidFeeTier();
if (twapWindow < MIN_TWAP_WINDOW || twapWindow > MAX_TWAP_WINDOW) {
revert InvalidTwapWindow();
}
PERMIT2 = permit2;
SWAP_ROUTER = swapRouter;
ROUTER_HAS_DEADLINE = routerHasDeadline;
WETH9 = weth9;
FACTORY = factory;
TREASURY = treasury;
POOL_FEE = poolFee;
TWAP_WINDOW = twapWindow;
_CACHED_CHAIN_ID = block.chainid;
_CACHED_THIS = address(this);
_CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator();
}
// --------------------------------------------------------------------- execute
/// @notice Execute one fill of a user-signed order: pull `fillAmount` of `tokenIn` via Permit2, route
/// it (BUY / SELL / CROSS — see {Route}), take EXACTLY the 1% WETH fee to the immutable
/// treasury, and forward everything else to `order.owner`. All-or-nothing; the router retains
/// nothing (tokenIn, tokenOut, WETH and native ETH residuals are all asserted zero).
/// @param order The user-signed order (terms + bounds).
/// @param orderSig EIP-712 signature over `order` by `order.owner` (recovered via ECDSA).
/// @param permitSingle Permit2 AllowanceTransfer permit funding this order (token, spender==router,
/// amount==totalBudget, expiration==endTime).
/// @param permitSig Owner's signature over `permitSingle` (submitted to Permit2 on first fill).
/// @param fillAmount Amount of `tokenIn` to pull and route this fill (<= maxAmountPerFill).
/// @param minOut Caller-supplied floor on the FINAL `tokenOut`; may only TIGHTEN the
/// on-chain-derived floor, never loosen it.
/// @return amountOut The NET `tokenOut` delivered to `order.owner` — the same value {OrderFilled}
/// reports, consistent across BUY / SELL / CROSS (see the event's note).
/// @dev Permissionless crank. CEI: all checks and state writes happen before any external interaction;
/// `nonReentrant` is belt-and-braces.
function execute(
Order calldata order,
bytes calldata orderSig,
IAllowanceTransfer.PermitSingle calldata permitSingle,
bytes calldata permitSig,
uint256 fillAmount,
uint256 minOut
) external nonReentrant returns (uint256 amountOut) {
// -------- CHECKS ------------------------------------------------------
bytes32 orderHash = _hashOrder(order);
if (cancelled[orderHash]) revert OrderCancelledError();
// (1) Order authorisation: recover the EIP-712 signer and require it == owner. Any malformed,
// malleable, or wrong-signer signature yields a uniform fail-closed BadOrderSignature.
(address signer, ECDSA.RecoverError err,) = ECDSA.tryRecover(orderHash, orderSig);
if (err != ECDSA.RecoverError.NoError || signer != order.owner) revert BadOrderSignature();
// (2) Static shape + the route. The route is DERIVED from the signed legs (never caller-supplied).
_validateShape(order);
Route route = _route(order.tokenIn, order.tokenOut);
// (3) The permit must fund THIS order's exact rail. (Permit2 also re-checks its own signature.)
if (permitSingle.details.token != order.tokenIn) revert PermitTokenMismatch();
if (permitSingle.spender != address(this)) revert PermitSpenderMismatch();
if (permitSingle.details.amount != order.totalBudget) revert PermitAmountMismatch();
if (permitSingle.details.expiration != uint48(order.endTime)) {
revert PermitExpirationMismatch();
}
// (4) Amount bounds.
if (fillAmount == 0) revert ZeroAmount();
if (fillAmount > order.maxAmountPerFill) revert FillTooLarge();
uint256 filled = cumulativeFilled[orderHash];
uint256 newCumulative = filled + fillAmount;
if (newCumulative > order.totalBudget) revert BudgetExceeded();
// (5) INTERVAL-GRIEFING GATE. `lastFillAt` is bumped by ANY successful fill, so on an
// interval-gated order a griefer could front-run every window with a 1-wei fill: the victim's
// real fill then reverts `IntervalNotElapsed` forever and the order never progresses (cost to
// the attacker: gas). Closed WITHOUT touching the signed {Order} struct by requiring an
// interval-gated fill to be a FULL slice — `maxAmountPerFill`, or the whole remaining budget
// on the final (smaller) slice. A dust fill can no longer consume the schedule slot; an
// attacker who wants to bump the timer must execute the user's ENTIRE intended slice, at the
// user's signed floor, with the output going to the USER — i.e. they can only do the user a
// favour and pay for it.
if (order.minIntervalSec != 0) {
uint256 remaining = order.totalBudget - filled;
uint256 slice = order.maxAmountPerFill < remaining ? order.maxAmountPerFill : remaining;
if (fillAmount < slice) revert FillTooSmall();
}
// (6) Schedule bounds. The interval gates only BETWEEN fills; the first fill (lastFillAt == 0)
// is governed solely by `startTime`.
if (block.timestamp < order.startTime) revert NotStarted();
if (block.timestamp > order.endTime) revert Expired();
uint256 last = lastFillAt[orderHash];
if (last != 0 && block.timestamp < last + order.minIntervalSec) {
revert IntervalNotElapsed();
}
// -------- EFFECTS -----------------------------------------------------
cumulativeFilled[orderHash] = newCumulative;
lastFillAt[orderHash] = block.timestamp;
// -------- INTERACTIONS ------------------------------------------------
uint256 ethBefore = address(this).balance; // native ETH is never expected; assert delta == 0.
// Pull exactly `fillAmount` of tokenIn from the owner via Permit2 (submits permit on first use).
_fund(order.owner, order.tokenIn, fillAmount, permitSingle, permitSig);
uint256 feeWeth;
uint256 amountInForSwap = fillAmount;
if (route == Route.BUY) {
// WETH leg = the input. Fee = 1% of input, taken BEFORE the swap (v1 behaviour, unchanged).
// Floor division ⇒ the truncated remainder is swapped for the USER.
feeWeth = (fillAmount * FEE_BPS) / BPS_DENOM;
IERC20(WETH9).safeTransfer(TREASURY, feeWeth);
amountInForSwap = fillAmount - feeWeth;
}
// SELL/CROSS: the FULL fill amount is swapped; the fee comes out of the WETH leg produced by the
// (first) swap.
// On-chain price/slippage floor on the FINAL tokenOut; caller `minOut` may only TIGHTEN it.
// Computed BEFORE any swap, so our own hops cannot influence the oracle read.
uint256 boundMinOut = _deriveMinOut(order, route, amountInForSwap);
if (minOut > boundMinOut) boundMinOut = minOut;
// SELL (tokenOut == WETH, which never taxes transfers) binds its floor to the GROSS WETH the
// router receives, so snapshot the router's WETH balance BEFORE the swap and measure the delta —
// byte-for-byte as v1. BUY/CROSS instead bind the floor to the OWNER's realised balance delta on
// delivery (see below), because an arbitrary tokenOut may tax its transfer; they need no pre-swap
// router snapshot here.
uint256 wethOutBefore;
if (route == Route.SELL) wethOutBefore = IERC20(WETH9).balanceOf(address(this));
uint256 wethMid;
uint256 hop2In;
if (route == Route.CROSS) {
// HOP 1: tokenIn -> WETH, with NO intermediate floor (see the contract-level note: the FINAL
// floor on hop 2 is the binding, non-gameable protection).
wethMid = _swap(order.tokenIn, WETH9, amountInForSwap, 0);
if (wethMid == 0) revert ZeroIntermediate();
// FEE: EXACTLY 1% of the RECEIVED WETH, floored (dust favours the user), to the treasury.
feeWeth = (wethMid * FEE_BPS) / BPS_DENOM;
if (feeWeth != 0) IERC20(WETH9).safeTransfer(TREASURY, feeWeth);
// HOP 2: the ENTIRE remaining intermediate WETH -> tokenOut, bound by the FINAL floor.
// fee + hop2In == wethMid exactly ⇒ zero WETH residual by construction.
hop2In = wethMid - feeWeth;
_swap(WETH9, order.tokenOut, hop2In, boundMinOut);
} else {
_swap(order.tokenIn, order.tokenOut, amountInForSwap, boundMinOut);
}
// FLOOR ENFORCEMENT — BOUND TO WHAT THE OWNER ACTUALLY RECEIVES.
// `SwapRouter02` enforces `amountOutMinimum` only against the amount the POOL reports, which is the
// PRE-transfer-tax figure. v1 (and the first V2 cut) then re-checked the ROUTER's MEASURED balance
// delta — an improvement over trusting the pool — but still delivered the tokenOut to the owner
// with a FINAL `_sweep`, and for a fee-on-transfer tokenOut that delivery transfer is taxed AGAIN.
// So the owner could net up to `taxBps` BELOW the floor they signed, while the event over-reported
// the pre-sweep amount. Fix: for an arbitrary (possibly fee-on-transfer) tokenOut, bind the floor
// to the owner's REALISED balance delta ACROSS the delivery — the only quantity that reflects what
// the user truly gets — and report that same delta as `amountOut`.
if (route == Route.SELL) {
// SELL: tokenOut == WETH, which never taxes transfers, so the owner-received amount equals
// `received - feeWeth` by construction — behaviour is byte-identical to v1. The floor binds the
// GROSS WETH the router received (measured against `wethOutBefore`); the 1% fee is then skimmed
// to the treasury and the owner nets the remainder, delivered by the WETH sweep below.
uint256 received = IERC20(WETH9).balanceOf(address(this)) - wethOutBefore;
if (received < boundMinOut) revert InsufficientOutput();
feeWeth = (received * FEE_BPS) / BPS_DENOM;
IERC20(WETH9).safeTransfer(TREASURY, feeWeth);
amountOut = received - feeWeth;
} else {
// BUY / CROSS: tokenOut is an ARBITRARY token that may TAX its own transfers. Deliver it FIRST,
// then bind the floor to the owner's realised balance delta across that delivery. We snapshot
// the owner's balance, sweep the FULL router balance to the owner (a full-balance sweep keeps
// the zero-residual post-condition intact — the router retains nothing, exactly as before, and
// a grief donation is paid to the user rather than bricking the fill), and require the realised
// delta to clear the floor (else `InsufficientOutput`). We keep the swap recipient as the
// router (not the owner) so the swap's own `amountOutMinimum == boundMinOut` guard is unchanged
// for non-taxing tokens and the final delivery — the only taxable hop we control — is measured
// directly. The delivery transfer runs inside this `nonReentrant` call, so a malicious tokenOut
// transfer hook cannot re-enter `execute` to disturb the measurement or double-spend.
uint256 ownerBefore = IERC20(order.tokenOut).balanceOf(order.owner);
_sweep(order.tokenOut, order.owner);
amountOut = IERC20(order.tokenOut).balanceOf(order.owner) - ownerBefore;
if (amountOut < boundMinOut) revert InsufficientOutput();
}
// Sweep any remaining tokenIn / WETH to the OWNER: the CROSS intermediate is fully consumed and the
// SELL output is delivered here, but full-balance sweeps guarantee zero residual even against a
// grief WETH/tokenIn donation (paid to the user, never able to brick a fill via the zero-residual
// post-condition). For BUY, tokenIn == WETH, so the second sweep is then a no-op.
_sweep(order.tokenIn, order.owner);
_sweep(WETH9, order.owner);
// -------- ZERO-RESIDUAL POST-CONDITION --------------------------------
if (IERC20(order.tokenIn).balanceOf(address(this)) != 0) revert TokenInResidual();
if (IERC20(order.tokenOut).balanceOf(address(this)) != 0) revert TokenOutResidual();
if (IERC20(WETH9).balanceOf(address(this)) != 0) revert WethResidual();
if (address(this).balance != ethBefore) revert EthResidual();
emit UltraFeeCollected(orderHash, order.owner, feeWeth);
if (route == Route.CROSS) {
emit CrossRouteFilled(
orderHash,
order.owner,
order.tokenIn,
order.tokenOut,
wethMid,
feeWeth,
hop2In,
amountOut
);
}
emit OrderFilled(
orderHash,
order.owner,
order.tokenIn,
order.tokenOut,
fillAmount,
amountOut,
feeWeth,
newCumulative
);
}
// --------------------------------------------------------------------- cancel
/// @notice Cancel an order; all future fills of it revert. Callable only by `order.owner`.
/// @dev THIS is the reliable kill-switch: checked first in `execute`, independent of Permit2, and it
/// works before the first fill. A bare Permit2 `approve(token, router, 0)` does NOT reliably
/// revoke an un-submitted permit (Permit2 leaves the nonce unbumped, so a later fill re-submits
/// the original permit); the frontend cancel MUST call this, or Permit2 `invalidateNonces`.
/// @param order The order to cancel.
/// @return orderHash The cancelled order's EIP-712 hash.
function cancelOrder(Order calldata order) external returns (bytes32 orderHash) {
if (msg.sender != order.owner) revert NotOwner();
orderHash = _hashOrder(order);
cancelled[orderHash] = true;
emit OrderCancelled(orderHash, order.owner);
}
// --------------------------------------------------------------------- views
/// @notice EIP-712 digest of an order (its canonical hash / storage key).
/// @param order The order to hash.
/// @return The EIP-712 typed-data hash.
function hashOrder(Order calldata order) external view returns (bytes32) {
return _hashOrder(order);
}
/// @notice The EIP-712 domain separator (for off-chain signing tooling).
function DOMAIN_SEPARATOR() external view returns (bytes32) {
return _domainSeparator();
}
/// @notice The route `execute` will take for a (tokenIn, tokenOut) pair — for keepers/UI previews.
/// @param tokenIn The order's input token.
/// @param tokenOut The order's output token.
/// @return The derived {Route} (BUY / SELL / CROSS). Reverts `IdenticalTokens` if the legs match.
function routeOf(address tokenIn, address tokenOut) external view returns (Route) {
return _route(tokenIn, tokenOut);
}
// --------------------------------------------------------------------- internal: validation
/// @dev Static (signer-independent) validation of an order's shape. Reverts fail-closed. Identical to
/// v1 except that the WETH-pair requirement is gone (it now lives in {_route}).
function _validateShape(Order calldata o) internal pure {
if (o.owner == address(0) || o.tokenIn == address(0) || o.tokenOut == address(0)) {
revert ZeroAddress();
}
if (o.orderType > uint8(OrderType.COUNTER)) revert InvalidOrderType();
if (o.maxSlippageBps > MAX_SLIPPAGE_BPS) revert SlippageTooHigh();
if (o.endTime <= o.startTime) revert BadWindow();
if (o.endTime > type(uint48).max) revert BadWindow(); // must fit the Permit2 uint48 expiration
if (o.totalBudget == 0 || o.maxAmountPerFill == 0) revert ZeroAmount();
if (o.totalBudget > type(uint160).max) revert BudgetTooLarge(); // must fit the Permit2 uint160
OrderType t = OrderType(o.orderType);
if (t == OrderType.LIMIT || t == OrderType.COUNTER) {
// Price-bounded orders MUST carry a price bound.
if (o.limitPriceX96 == 0) revert PriceBoundRequired();
} else {
// MARKET/DCA must carry SOME on-chain price protection (slippage and/or a price bound).
if (o.maxSlippageBps == 0 && o.limitPriceX96 == 0) revert PriceBoundRequired();
}
if (t == OrderType.DCA && o.minIntervalSec == 0) revert IntervalRequired();
}
/// @dev Derive the execution route from the signed legs. Pure function of (tokenIn, tokenOut, WETH9):
/// no caller input, so nobody can choose a different (cheaper/looser) route than the tokens imply.
/// `tokenIn == tokenOut` (including WETH→WETH) is rejected — there is nothing to trade.
function _route(address tokenIn, address tokenOut) internal view returns (Route) {
if (tokenIn == tokenOut) revert IdenticalTokens();
if (tokenIn == WETH9) return Route.BUY;
if (tokenOut == WETH9) return Route.SELL;
return Route.CROSS; // token → WETH → token
}
// --------------------------------------------------------------------- internal: funding
/// @dev Pull `amount` of `token` from `owner` to this router via Permit2. Submits the user's permit
/// on first use (when the stored allowance is insufficient or expired) then transfers. If the
/// user has revoked (or the nonce was consumed), the permit re-submit reverts — fail-closed.
function _fund(
address owner,
address token,
uint256 amount,
IAllowanceTransfer.PermitSingle calldata permitSingle,
bytes calldata permitSig
) internal {
(uint160 allowed, uint48 exp,) = PERMIT2.allowance(owner, token, address(this));
if (allowed < amount || exp < block.timestamp) {
PERMIT2.permit(owner, permitSingle, permitSig);
}
PERMIT2.transferFrom(owner, address(this), amount.toUint160(), token);
}
// --------------------------------------------------------------------- internal: pricing
/// @dev Derive the binding floor on the FINAL `tokenOut` from the user's SIGNED constraints only:
/// - `limitPriceX96` (min tokenOut-per-tokenIn, Q96) — oracle-free, used by LIMIT/COUNTER, and
/// - `maxSlippageBps` vs. the pool TWAP(s) — used by MARKET/DCA.
/// The STRICTER (max) of any present floors binds; at least one must be > 0 (fail-closed), so a
/// caller can never be the sole source of price protection.
///
/// TWO CORRECTIONS THE CONTRACT OWNS (so the frontend owns NEITHER):
///
/// (1) POOL FEES. `_twapAmountOut` returns a MID price — it does NOT include the fee the pool
/// charges on the swap. A BUY/SELL pays {POOL_FEE} once; a CROSS pays it TWICE. Building the
/// floor on the raw mid would force `maxSlippageBps` to silently absorb those pool fees, and
/// a CROSS would need ~199 bps just to break even at ZERO price impact — i.e. the UI's tight
/// presets could never fill. Each leg's TWAP is therefore discounted by that leg's pool fee
/// HERE, so `maxSlippageBps` is a pure price-impact/drift tolerance in every route.
///
/// (2) THE ULTRA FEE, so `limitPriceX96` means ONE thing in all three routes. On BUY the 1% is
/// skimmed off the swap input (`amountInForSwap` is already net) and on SELL it is skimmed off
/// the output, so both deliver `0.99 * limitPriceX96` per GROSS tokenIn. On CROSS the fee is
/// taken mid-route and `amountInForSwap` is the GROSS amount, so the basis is netted by 1%
/// here to match. Result: `limitPriceX96` always guarantees `0.99 * L` tokenOut per gross
/// tokenIn — v1's frontend limit math keeps working, unchanged, on every route.
function _deriveMinOut(Order calldata o, Route route, uint256 amountInForSwap)
internal
view
returns (uint256 floor)
{
if (o.limitPriceX96 != 0) {
// (2) Normalise the basis so all three routes mean the same thing.
uint256 basis = amountInForSwap;
if (route == Route.CROSS) basis -= (basis * FEE_BPS) / BPS_DENOM;
floor = Math.mulDiv(basis, o.limitPriceX96, Q96);
}
if (o.maxSlippageBps != 0) {
uint256 twapOut;
if (route == Route.CROSS) {
// Leg 1: tokenIn -> WETH at pool-1's TWAP (gated), less pool-1's swap fee.
uint256 wethMid = _twapAmountOut(o.tokenIn, WETH9, amountInForSwap);
wethMid -= Math.mulDiv(wethMid, uint256(POOL_FEE), POOL_FEE_DENOM);
// The 1% Ultra WETH fee is taken from the intermediate, so only 99% funds leg 2.
uint256 afterFee = wethMid - (wethMid * FEE_BPS) / BPS_DENOM;
// Leg 2: WETH -> tokenOut at pool-2's TWAP (gated independently), less pool-2's swap fee.
twapOut = _twapAmountOut(WETH9, o.tokenOut, afterFee);
twapOut -= Math.mulDiv(twapOut, uint256(POOL_FEE), POOL_FEE_DENOM);
} else {
twapOut = _twapAmountOut(o.tokenIn, o.tokenOut, amountInForSwap);
twapOut -= Math.mulDiv(twapOut, uint256(POOL_FEE), POOL_FEE_DENOM);
}
uint256 slipFloor = Math.mulDiv(twapOut, BPS_DENOM - o.maxSlippageBps, BPS_DENOM);
if (slipFloor > floor) floor = slipFloor;
}
if (floor == 0) revert NoOnChainFloor();
}
/// @dev `tokenOut` out for `amountIn` priced at the pool's TIME-WEIGHTED average tick over
/// {TWAP_WINDOW} (via `observe`), NEVER the single-block `slot0` spot. UNCHANGED from v1 — the
/// oracle-quality gate is NOT weakened for CROSS; it is simply applied TWICE (once per pool), so
/// a CROSS MARKET/DCA fill requires BOTH pools to have a genuine, recently-anchored TWAP.
/// Fails closed with `PoolNotFound` / `OracleNotReady`; never falls back to slot0.
function _twapAmountOut(address tokenIn, address tokenOut, uint256 amountIn)
internal
view
returns (uint256)
{
address pool = FACTORY.getPool(tokenIn, tokenOut, POOL_FEE);
if (pool == address(0)) revert PoolNotFound();
// ORACLE-QUALITY GATE (fail-closed).
// (1) Cardinality must be ≥ MIN so the ring buffer can bracket the window with real snapshots.
// (2) The newest observation must be RECENT (≤ window/MAX_EXTRAP_DIVISOR old) so the extrapolated
// tail is a small fraction of the mean — this defeats the "manipulate spot, wait a full idle
// window, then fill at the extrapolated (== spot) price" attack.
// (3) `observe` reverting 'OLD' (no snapshot ≥ window ago) also fails closed.
(,, uint16 obsIndex, uint16 obsCardinality,,,) = IUniswapV3Pool(pool).slot0();
if (obsCardinality < MIN_ORACLE_CARDINALITY) revert OracleNotReady();
(uint32 newestTs,,, bool initialized) = IUniswapV3Pool(pool).observations(obsIndex);
if (!initialized) revert OracleNotReady();
if (block.timestamp - uint256(newestTs) > uint256(TWAP_WINDOW) / MAX_EXTRAP_DIVISOR) {
revert OracleNotReady();
}
uint32[] memory secondsAgos = new uint32[](2);
secondsAgos[0] = TWAP_WINDOW; // window seconds ago
secondsAgos[1] = 0; // now
int56[] memory tickCumulatives;
try IUniswapV3Pool(pool).observe(secondsAgos) returns (
int56[] memory tc, uint160[] memory
) {
tickCumulatives = tc;
} catch {
revert OracleNotReady();
}
int24 meanTick = OracleTwap.arithmeticMeanTick(tickCumulatives, TWAP_WINDOW);
return OracleTwap.getQuoteAtTick(meanTick, amountIn, tokenIn, tokenOut);
}
// --------------------------------------------------------------------- internal: swap + sweep
/// @dev Approve exactly `amountIn`, swap `tokenIn -> tokenOut` to THIS router, then zero the approval
/// (NO dangling allowance — true for BOTH hops of a CROSS route, which calls this twice).
/// Reverts if the pool cannot deliver `minOut`. `sqrtPriceLimitX96 == 0` (minOut is the guard).
function _swap(address tokenIn, address tokenOut, uint256 amountIn, uint256 minOut)
internal
returns (uint256 amountOut)
{
IERC20(tokenIn).forceApprove(address(SWAP_ROUTER), amountIn);
if (ROUTER_HAS_DEADLINE) {
amountOut = ISwapRouterV1(address(SWAP_ROUTER))
.exactInputSingle(
ISwapRouterV1.ExactInputSingleParams({
tokenIn: tokenIn,
tokenOut: tokenOut,
fee: POOL_FEE,
recipient: address(this),
deadline: block.timestamp,
amountIn: amountIn,
amountOutMinimum: minOut,
sqrtPriceLimitX96: 0
})
);
} else {
amountOut = SWAP_ROUTER.exactInputSingle(
ISwapRouter02.ExactInputSingleParams({
tokenIn: tokenIn,
tokenOut: tokenOut,
fee: POOL_FEE,
recipient: address(this),
amountIn: amountIn,
amountOutMinimum: minOut,
sqrtPriceLimitX96: 0
})
);
}
IERC20(tokenIn).forceApprove(address(SWAP_ROUTER), 0);
}
/// @dev Send the router's ENTIRE `token` balance to `to`. Sweeping the full balance (not a computed
/// amount) guarantees zero residual even if someone grief-sends dust into the router.
function _sweep(address token, address to) internal {
uint256 bal = IERC20(token).balanceOf(address(this));
if (bal != 0) IERC20(token).safeTransfer(to, bal);
}
// --------------------------------------------------------------------- internal: hashing
/// @dev EIP-712 digest of an order. Identical construction (and typehash) to v1.
function _hashOrder(Order calldata o) internal view returns (bytes32) {
bytes32 structHash = keccak256(
abi.encode(
ORDER_TYPEHASH,
o.owner,
o.tokenIn,
o.tokenOut,
o.orderType,
o.maxAmountPerFill,
o.minIntervalSec,
o.startTime,
o.endTime,
o.totalBudget,
o.limitPriceX96,
o.maxSlippageBps,
o.salt
)
);
return keccak256(abi.encodePacked(hex"1901", _domainSeparator(), structHash));
}
/// @dev The current EIP-712 domain separator, rebuilt if the chain forked since construction.
function _domainSeparator() internal view returns (bytes32) {
if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
return _CACHED_DOMAIN_SEPARATOR;
}
return _buildDomainSeparator();
}
/// @dev Build the EIP-712 domain separator for the current chain + address.
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(
abi.encode(
_DOMAIN_TYPEHASH, _HASHED_NAME, _HASHED_VERSION, block.chainid, address(this)
)
);
}
}[
{
"type": "constructor",
"inputs": [
{
"name": "permit2",
"type": "address",
"internalType": "contract IAllowanceTransfer"
},
{
"name": "swapRouter",
"type": "address",
"internalType": "contract ISwapRouter02"
},
{
"name": "weth9",
"type": "address",
"internalType": "address"
},
{
"name": "factory",
"type": "address",
"internalType": "contract IUniswapV3Factory"
},
{
"name": "treasury",
"type": "address",
"internalType": "address"
},
{
"name": "poolFee",
"type": "uint24",
"internalType": "uint24"
},
{
"name": "routerHasDeadline",
"type": "bool",
"internalType": "bool"
},
{
"name": "twapWindow",
"type": "uint32",
"internalType": "uint32"
}
],
"stateMutability": "nonpayable"
},
{
"name": "BadOrderSignature",
"type": "error",
"inputs": []
},
{
"name": "BadWindow",
"type": "error",
"inputs": []
},
{
"name": "BudgetExceeded",
"type": "error",
"inputs": []
},
{
"name": "BudgetTooLarge",
"type": "error",
"inputs": []
},
{
"name": "EthResidual",
"type": "error",
"inputs": []
},
{
"name": "Expired",
"type": "error",
"inputs": []
},
{
"name": "FillTooLarge",
"type": "error",
"inputs": []
},
{
"name": "FillTooSmall",
"type": "error",
"inputs": []
},
{
"name": "IdenticalTokens",
"type": "error",
"inputs": []
},
{
"name": "InsufficientOutput",
"type": "error",
"inputs": []
},
{
"name": "IntervalNotElapsed",
"type": "error",
"inputs": []
},
{
"name": "IntervalRequired",
"type": "error",
"inputs": []
},
{
"name": "InvalidFeeTier",
"type": "error",
"inputs": []
},
{
"name": "InvalidOrderType",
"type": "error",
"inputs": []
},
{
"name": "InvalidTwapWindow",
"type": "error",
"inputs": []
},
{
"name": "NoOnChainFloor",
"type": "error",
"inputs": []
},
{
"name": "NotOwner",
"type": "error",
"inputs": []
},
{
"name": "NotStarted",
"type": "error",
"inputs": []
},
{
"name": "OracleNotReady",
"type": "error",
"inputs": []
},
{
"name": "OrderCancelledError",
"type": "error",
"inputs": []
},
{
"name": "PermitAmountMismatch",
"type": "error",
"inputs": []
},
{
"name": "PermitExpirationMismatch",
"type": "error",
"inputs": []
},
{
"name": "PermitSpenderMismatch",
"type": "error",
"inputs": []
},
{
"name": "PermitTokenMismatch",
"type": "error",
"inputs": []
},
{
"name": "PoolNotFound",
"type": "error",
"inputs": []
},
{
"name": "PriceBoundRequired",
"type": "error",
"inputs": []
},
{
"name": "ReentrancyGuardReentrantCall",
"type": "error",
"inputs": []
},
{
"name": "SafeCastOverflowedUintDowncast",
"type": "error",
"inputs": [
{
"name": "bits",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "value",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "SafeERC20FailedOperation",
"type": "error",
"inputs": [
{
"name": "token",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "SlippageTooHigh",
"type": "error",
"inputs": []
},
{
"name": "TickOutOfRange",
"type": "error",
"inputs": []
},
{
"name": "TokenInResidual",
"type": "error",
"inputs": []
},
{
"name": "TokenOutResidual",
"type": "error",
"inputs": []
},
{
"name": "WethResidual",
"type": "error",
"inputs": []
},
{
"name": "ZeroAddress",
"type": "error",
"inputs": []
},
{
"name": "ZeroAmount",
"type": "error",
"inputs": []
},
{
"name": "ZeroIntermediate",
"type": "error",
"inputs": []
},
{
"name": "CrossRouteFilled",
"type": "event",
"inputs": [
{
"name": "orderHash",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "owner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokenIn",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokenOut",
"type": "address",
"indexed": false,
"internalType": "address"
},
{
"name": "wethMid",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "feeWeth",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "hop2In",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "amountOut",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "OrderCancelled",
"type": "event",
"inputs": [
{
"name": "orderHash",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "owner",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "OrderFilled",
"type": "event",
"inputs": [
{
"name": "orderHash",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "owner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokenIn",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokenOut",
"type": "address",
"indexed": false,
"internalType": "address"
},
{
"name": "amountIn",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "amountOut",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "feeWeth",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "cumulativeFilled",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "UltraFeeCollected",
"type": "event",
"inputs": [
{
"name": "orderHash",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "owner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "feeWeth",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "BPS_DENOM",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "DOMAIN_SEPARATOR",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"name": "FACTORY",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "contract IUniswapV3Factory"
}
],
"stateMutability": "view"
},
{
"name": "FEE_BPS",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "MAX_SLIPPAGE_BPS",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "MAX_TWAP_WINDOW",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint32",
"internalType": "uint32"
}
],
"stateMutability": "view"
},
{
"name": "MIN_TWAP_WINDOW",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint32",
"internalType": "uint32"
}
],
"stateMutability": "view"
},
{
"name": "ORDER_TYPEHASH",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"name": "PERMIT2",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "contract IAllowanceTransfer"
}
],
"stateMutability": "view"
},
{
"name": "POOL_FEE",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint24",
"internalType": "uint24"
}
],
"stateMutability": "view"
},
{
"name": "ROUTER_HAS_DEADLINE",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "SWAP_ROUTER",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "contract ISwapRouter02"
}
],
"stateMutability": "view"
},
{
"name": "TREASURY",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "TWAP_WINDOW",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint32",
"internalType": "uint32"
}
],
"stateMutability": "view"
},
{
"name": "WETH9",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "cancelOrder",
"type": "function",
"inputs": [
{
"name": "order",
"type": "tuple",
"components": [
{
"name": "owner",
"type": "address",
"internalType": "address"
},
{
"name": "tokenIn",
"type": "address",
"internalType": "address"
},
{
"name": "tokenOut",
"type": "address",
"internalType": "address"
},
{
"name": "orderType",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "maxAmountPerFill",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "minIntervalSec",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "startTime",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "endTime",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "totalBudget",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "limitPriceX96",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "maxSlippageBps",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "salt",
"type": "uint256",
"internalType": "uint256"
}
],
"internalType": "struct NasdankOrderRouterV2.Order"
}
],
"outputs": [
{
"name": "orderHash",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "nonpayable"
},
{
"name": "cancelled",
"type": "function",
"inputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "cumulativeFilled",
"type": "function",
"inputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "execute",
"type": "function",
"inputs": [
{
"name": "order",
"type": "tuple",
"components": [
{
"name": "owner",
"type": "address",
"internalType": "address"
},
{
"name": "tokenIn",
"type": "address",
"internalType": "address"
},
{
"name": "tokenOut",
"type": "address",
"internalType": "address"
},
{
"name": "orderType",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "maxAmountPerFill",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "minIntervalSec",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "startTime",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "endTime",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "totalBudget",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "limitPriceX96",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "maxSlippageBps",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "salt",
"type": "uint256",
"internalType": "uint256"
}
],
"internalType": "struct NasdankOrderRouterV2.Order"
},
{
"name": "orderSig",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "permitSingle",
"type": "tuple",
"components": [
{
"name": "details",
"type": "tuple",
"components": [
{
"name": "token",
"type": "address",
"internalType": "address"
},
{
"name": "amount",
"type": "uint160",
"internalType": "uint160"
},
{
"name": "expiration",
"type": "uint48",
"internalType": "uint48"
},
{
"name": "nonce",
"type": "uint48",
"internalType": "uint48"
}
],
"internalType": "struct IAllowanceTransfer.PermitDetails"
},
{
"name": "spender",
"type": "address",
"internalType": "address"
},
{
"name": "sigDeadline",
"type": "uint256",
"internalType": "uint256"
}
],
"internalType": "struct IAllowanceTransfer.PermitSingle"
},
{
"name": "permitSig",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "fillAmount",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "minOut",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "amountOut",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "nonpayable"
},
{
"name": "hashOrder",
"type": "function",
"inputs": [
{
"name": "order",
"type": "tuple",
"components": [
{
"name": "owner",
"type": "address",
"internalType": "address"
},
{
"name": "tokenIn",
"type": "address",
"internalType": "address"
},
{
"name": "tokenOut",
"type": "address",
"internalType": "address"
},
{
"name": "orderType",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "maxAmountPerFill",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "minIntervalSec",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "startTime",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "endTime",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "totalBudget",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "limitPriceX96",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "maxSlippageBps",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "salt",
"type": "uint256",
"internalType": "uint256"
}
],
"internalType": "struct NasdankOrderRouterV2.Order"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"name": "lastFillAt",
"type": "function",
"inputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "routeOf",
"type": "function",
"inputs": [
{
"name": "tokenIn",
"type": "address",
"internalType": "address"
},
{
"name": "tokenOut",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "uint8",
"internalType": "enum NasdankOrderRouterV2.Route"
}
],
"stateMutability": "view"
}
]0x6101e06040818152346200046457816200394f803803809162000023828562000469565b833961010093849181010312620004645780516001600160a01b03919082811690818103620004645760209283810151908582169485830362000464576200006d8883016200048d565b926060830151948886169687870362000464576200008e608086016200048d565b9760a08601519962ffffff8b1690818c03620004645760c0880151978815158903620004645760e001519c63ffffffff8e1694858f03620004645760017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055159182156200045a575b5081156200044e575b811562000444575b811562000438575b506200042757859060248e51809481936322afcccb60e01b835260048301525afa9081156200041c57600091620003d7575b5060020b15620003c657603c8110908115620003b9575b50620003a85760e05289526101209182526101409283526101609384526101809485526101a09586526101c09687524660a0523060c0528751908101907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82527f334838d273155aa64e72dba96b6e08ad1f6a9b77f41ef381199d6bd8b7d8b938898201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815260c0810198818a1060018060401b038b111762000392578990525190206080526134ac9788620004a3893960805188611f21015260a05188611f46015260c05188611e45015260e051888181610764015281816107d001528181610842015281816116cc015281816117980152611b340152518781816102be015281816108ec01528181610a3001528181610a71015281816112c101526122050152518681816103a701528181610917015261223701525185818161095801528181610ad301528181610b1e01528181610bad01528181610c0701528181610cc80152818161114b015281816111fa015281816113770152818161143f0152818161161901528181611bbf01526120ff015251848181611c1f0152612946015251838181610b8c0152818161112a015281816115f80152611c6401525182818161028401528181610987015281816112290152818161146f0152818161155b0152818161228001528181612396015261290e0152518181816102020152612a140152f35b634e487b7160e01b600052604160045260246000fd5b8951635df6da8560e11b8152600490fd5b610e109150113862000159565b8a51631b99aa4560e11b8152600490fd5b8581813d831162000414575b620003ef818362000469565b81010312620004105751908160020b82036200040d57503862000142565b80fd5b5080fd5b503d620003e3565b8c513d6000823e3d90fd5b8c5163d92e233d60e01b8152600490fd5b90508a16153862000110565b8315915062000108565b89811615915062000100565b15915038620000f7565b600080fd5b601f909101601f19168101906001600160401b038211908210176200039257604052565b51906001600160a01b0382168203620004645756fe608080604052600436101561001357600080fd5b600090813560e01c9081631a65893b14611cc2575080632ac1262214611c935780632d2c556514611c4e5780632dd3100014611c095780633644e51514611bee5780634aa4a4fc14611ba95780635d6c499914611b805780636637e38c14611b635780636afdd85014611b1e5780637e2140ef14611af45780638239ed75146103cc57806385a505f51461038f578063b4b01a5514610326578063b88a92b414610309578063bf333f2c146102ed578063c6005893146102a8578063dd1b9c4a14610268578063dff612c014610243578063e229cd7614610226578063e2c1d1d1146101e5578063f6e8bdf3146101505763f973a2091461011357600080fd5b3461014d578060031936011261014d5760206040517fcc78d70c3a92f4a3b3950ed2991fce6825158116644c7f4952935372197ef2a28152f35b80fd5b503461014d5761018036600319011261014d576001600160a01b039081610175611db0565b1633036101d357602091610187611f7c565b8083526002845260408320805460ff19166001179055906101a6611db0565b16817fa6eb7cdc219e1518ced964e9a34e61d68a94e4f1569db3e84256ba981ba527536040519480a38152f35b6040516330cd747160e01b8152600490fd5b503461014d578060031936011261014d57602060405163ffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461014d578060031936011261014d5760206040516113888152f35b503461014d5761018036600319011261014d576020610260611f7c565b604051908152f35b503461014d578060031936011261014d57602060405162ffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461014d578060031936011261014d576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461014d578060031936011261014d57602060405160648152f35b503461014d578060031936011261014d576020604051610e108152f35b503461014d57604036600319011261014d576004356001600160a01b03808216820361038a57602435908116810361038a57610361916120e7565b60405190600381101561037657602092508152f35b634e487b7160e01b83526021600452602483fd5b600080fd5b503461014d578060031936011261014d5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b503461014d5736600319016102c08112611af0576101801361014d5767ffffffffffffffff9061018435828111611af05761040b903690600401611cdd565b60c0366101a3190112611aec576102643584811161169557610431903690600401611cdd565b92909160027f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005414611ada5760027f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055610489611f7c565b95868652600260205260ff604087205416611ac8578111611ab457604051916104bc601f8301601f191660200184611d6e565b81835236828201116116a45760208284938893836104e097013783010152856120ab565b506104ea81611d90565b1590811591611a96575b50611a84576001600160a01b03610509611db0565b16158015611a6c575b8015611a54575b611a4257600360ff610529611f6c565b1611611a30576113886101443511611a1e5760c43560e4351115611a0c5765ffffffffffff60e43511611a0c5761010435158015611a02575b611927576001600160a01b0361010435116119f05760ff610581611f6c565b1661058b81611d90565b61059481611d90565b6001811480156119dd575b156119bb5761012435156119a9576002905b6105ba81611d90565b148061199f575b61198d576105de6105d0611dc6565b6105d8611ddc565b906120e7565b936101a435916001600160a01b0383168303611989576001600160a01b03610604611dc6565b166001600160a01b038416036119775761022435926001600160a01b03841684036116a457306001600160a01b03851603611965576101c435906001600160a01b038216820361196157610104356001600160a01b0383160361194f576101e4359265ffffffffffff8416840361194b5765ffffffffffff60e4351665ffffffffffff85160361193957610284351561192757608435610284351161191557848852876020526040882054966106bd6102843589611df2565b976101043589116119035760a4356118bf575b5060c43542106118ad5760e435421161189b57858952600160205260408920548015159081611885575b5061187357858952886020528760408a205560016020524260408a20554796610721611db0565b9561072a611dc6565b60405163927da10560e01b81526001600160a01b0389811660048301528083166024830152306044830152919791606090829060649082907f0000000000000000000000000000000000000000000000000000000000000000165afa908115611868578d908192611810575b50610284356001600160a01b03909116109081156117fd575b506116ca575b50506001600160a01b03610284351193506116a892505050577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163b156116a457604051631b63c28b60e11b81526001600160a01b039283166004820152306024820152610284358316604482015290821660648201529085908290608490829084907f0000000000000000000000000000000000000000000000000000000000000000165af1801561169957908591611681575b50508391610284356003871015918261166d5787156115d5575b86946101243580611586575b5061014435611424575b851561141257856102a43511611407575b87936113f35760018914611359575b879081908760028c0361133457505050506108db611dc6565b916000610915826001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811690871661332c565b7f0000000000000000000000000000000000000000000000000000000000000000156111de57506040519061094982611d51565b6001600160a01b0384811683527f00000000000000000000000000000000000000000000000000000000000000008116602080850191825262ffffff7f00000000000000000000000000000000000000000000000000000000000000008116604080880191825230606089019081524260808a0190815260a08a01988952600060c08b0181815260e08c01828152945163414bf38960e01b81529b518a1660048d01529751891660248c0152935190941660448a0152518616606489015291516084880152945160a4870152915160c48601529051821660e48501529183916101049183917f0000000000000000000000000000000000000000000000000000000000000000165af180156111d25760009061119e575b610a999150925b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911661327a565b8192821561118c576064830283810460640361117857612710610ac3910480948161112457611e15565b92610af78785610ad1611ddc565b7f00000000000000000000000000000000000000000000000000000000000000006121f6565b505b60018a03610fdf57506040516370a0823160e01b8152306004820152906020826024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa8015610fd4578990610fa0575b610b5f9250611e15565b948510610f8e5784606481020460641485151715610f7a57612710606486020494610bdf908690610bda827f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612175565b611e15565b965b610bfa610bec611dc6565b610bf4611db0565b9061243c565b610c2b610c05611db0565b7f000000000000000000000000000000000000000000000000000000000000000061243c565b602460206001600160a01b03610c3f611dc6565b16604051928380926370a0823160e01b82523060048301525afa908115610f2b578291610f48575b50610f3657602460206001600160a01b03610c80611ddc565b16604051928380926370a0823160e01b82523060048301525afa908115610f2b578291610ef9575b50610ee7576040516370a0823160e01b81523060048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa918215610edb5791610ea9575b50610e97574703610e85576020966002906001600160a01b03610d20611db0565b16857fe05bbd37cea5189e100363500e1fb455cb1287068abf68b76255b512c89ac5388b6040518a8152a314610e00575b50507f7fb08f5e950a93a3b88eba4ac327d81cabdf470b33827529e6378da11ed6575684610d7d611db0565b94610dd1610d89611dc6565b91610d92611ddc565b604080516001600160a01b03928316815261028435602082015290810195909552606085019790975260808401529085169590941693819060a0820190565b0390a460017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055604051908152f35b827fb0e901624fd264d4477ece69c5e885ba3f97f8a5e43ba2aef0e4e5f23615d944610e2a611db0565b93610e33611dc6565b610e7b8a610e3f611ddc565b604080516001600160a01b039283168152602081019690965285018b9052606085019790975260808401529085169590941693819060a0820190565b0390a43880610d51565b6040516355fef2db60e01b8152600490fd5b6040516310c39b9d60e21b8152600490fd5b90506020813d602011610ed3575b81610ec460209383611d6e565b8101031261038a575138610cff565b3d9150610eb7565b604051903d90823e3d90fd5b604051638dd38fad60e01b8152600490fd5b90506020813d602011610f23575b81610f1460209383611d6e565b8101031261038a575138610ca8565b3d9150610f07565b6040513d84823e3d90fd5b604051631be2414760e11b8152600490fd5b90506020813d602011610f72575b81610f6360209383611d6e565b8101031261038a575138610c67565b3d9150610f56565b634e487b7160e01b87526011600452602487fd5b60405163bb2875c360e01b8152600490fd5b506020823d602011610fcc575b81610fba60209383611d6e565b8101031261038a57610b5f9151610b55565b3d9150610fad565b6040513d8b823e3d90fd5b959790506001600160a01b03610ff3611ddc565b166020610ffe611db0565b6040516370a0823160e01b81526001600160a01b03909116600482015291829060249082905afa908115610f2b5782916110f2575b5061103f610bec611ddc565b6001600160a01b0361104f611ddc565b1690602061105b611db0565b6040516370a0823160e01b81526001600160a01b03909116600482015292839060249082905afa80156110e75783906110b3575b6110999250611e15565b97881015610be15760405163bb2875c360e01b8152600490fd5b506020823d6020116110df575b816110cd60209383611d6e565b8101031261038a57611099915161108f565b3d91506110c0565b6040513d85823e3d90fd5b90506020813d60201161111c575b8161110d60209383611d6e565b8101031261038a575138611033565b3d9150611100565b610bda827f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612175565b634e487b7160e01b89526011600452602489fd5b60405163210b89a160e21b8152600490fd5b506020813d6020116111ca575b816111b860209383611d6e565b8101031261038a57610a999051610a60565b3d91506111ab565b6040513d6000823e3d90fd5b604051916111eb83611d35565b6001600160a01b0385811684527f00000000000000000000000000000000000000000000000000000000000000008116602080860191825262ffffff7f0000000000000000000000000000000000000000000000000000000000000000811660408089019182523060608a0190815260808a0197885260a08a0189815260c08b018a815292516304e45aaf60e01b81529a51881660048c01529551871660248b015291519092166044890152518416606488015293516084870152905160a48601529151811660c4850152839060e490829085907f0000000000000000000000000000000000000000000000000000000000000000165af1908115610edb5780916112fd575b50610a99915092610a67565b90506020823d60201161132c575b8161131860209383611d6e565b8101031261014d5750610a999051386112f1565b3d915061130b565b611353919295611345979497611dc6565b61134d611ddc565b906121f6565b50610af9565b6040516370a0823160e01b81523060048201529093506020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156113e85788916113b6575b50926108c2565b90506020813d6020116113e0575b816113d160209383611d6e565b8101031261038a5751386113af565b3d91506113c4565b6040513d8a823e3d90fd5b634e487b7160e01b88526021600452602488fd5b6102a43595506108b3565b604051633e41d18160e21b8152600490fd5b836113f3576002890361153b57611439611dc6565b611465847f000000000000000000000000000000000000000000000000000000000000000080936128e7565b906114a062ffffff7f0000000000000000000000000000000000000000000000000000000000000000169261149a8482612598565b90611e15565b6064810281810460641482151715611527576114e293926114cc61149a936127106114db940490611e15565b906114d5611ddc565b906128e7565b9182612598565b6127109061014435820391821161151357906114fd91612602565b86811161150b575b506108a2565b955038611505565b634e487b7160e01b8a52601160045260248afd5b634e487b7160e01b8c52601160045260248cfd5b6115816115528461154a611dc6565b6114d5611ddc565b61149a62ffffff7f00000000000000000000000000000000000000000000000000000000000000001682612598565b6114e2565b909550600093508260028a146115a8575b906115a191612540565b9438610898565b50606483028381046064148415171561117857906115cd6127106115a1930485611e15565b909150611597565b9350506064610284350261028435810460640361165957612710900492611646847f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612175565b6116538461028435611e15565b9061088c565b634e487b7160e01b86526011600452602486fd5b634e487b7160e01b87526021600452602487fd5b61168a90611d0b565b611695578338610872565b8380fd5b6040513d87823e3d90fd5b8580fd5b6040516306dfcc6560e41b815260a06004820152610284356024820152604490fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163b156117f9576040516302b67b5760e41b81526001600160a01b03898116600483015296871660248201529516604486015265ffffffffffff908116606486015261020435908116908190036117f55760848501526001600160a01b031660a48401526102443560c484015261010060e4840152610104830181905288918391610124918391908190848401378181018301859052601f01601f19168101030181837f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af180156117ea576117d7575b80808080806107b5565b6117e390969196611d0b565b94386117cd565b6040513d89823e3d90fd5b8a80fd5b8b80fd5b905065ffffffffffff42911610386107af565b9150506060813d606011611860575b8161182c60609383611d6e565b8101031261185c5761183d8161214e565b611855604061184e60208501612162565b9301612162565b5038610796565b8c80fd5b3d915061181f565b6040513d8f823e3d90fd5b60405163c885103360e01b8152600490fd5b611893915060a43590611df2565b4210386106fa565b604051630407b05b60e31b8152600490fd5b604051636f312cbd60e01b8152600490fd5b6118cc9061010435611e15565b80608435106000146118fe57506084355b61028435106118ec57386106d0565b604051636b7b340160e11b8152600490fd5b6118dd565b6040516350b2c4e160e01b8152600490fd5b6040516333fa08af60e21b8152600490fd5b604051631f2a200560e01b8152600490fd5b604051632f491ad760e11b8152600490fd5b8780fd5b604051633b8ccc8f60e11b8152600490fd5b8680fd5b60405163478df8db60e11b8152600490fd5b6040516336273b4b60e11b8152600490fd5b8480fd5b6040516365851d6560e01b8152600490fd5b5060a435156105c1565b6040516343cca87f60e01b8152600490fd5b6101443515806119d2575b6119a9576002906105b1565b5061012435156119c6565b506119e781611d90565b6003811461059f565b60405163feaa6cdb60e01b8152600490fd5b5060843515610562565b604051632a0c9bb560e11b8152600490fd5b60405163428637bb60e11b8152600490fd5b60405163688c176f60e01b8152600490fd5b60405163d92e233d60e01b8152600490fd5b506001600160a01b03611a65611ddc565b1615610519565b506001600160a01b03611a7d611dc6565b1615610512565b60405163954095ab60e01b8152600490fd5b90506001600160a01b0380611aa9611db0565b1691161415386104f4565b634e487b7160e01b85526041600452602485fd5b6040516374ca404360e01b8152600490fd5b604051633ee5aeb560e01b8152600490fd5b8280fd5b5080fd5b503461014d57602036600319011261014d5760406020916004358152600183522054604051908152f35b503461014d578060031936011261014d576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461014d578060031936011261014d5760206040516127108152f35b503461014d57602036600319011261014d57604060209160043581528083522054604051908152f35b503461014d578060031936011261014d576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461014d578060031936011261014d576020610260611e42565b503461014d578060031936011261014d576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461014d578060031936011261014d576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461014d57602036600319011261014d5760ff60406020926004358152600284522054166040519015158152f35b905034611af05781600319360112611af05780603c60209252f35b9181601f8401121561038a5782359167ffffffffffffffff831161038a576020838186019501011161038a57565b67ffffffffffffffff8111611d1f57604052565b634e487b7160e01b600052604160045260246000fd5b60e0810190811067ffffffffffffffff821117611d1f57604052565b610100810190811067ffffffffffffffff821117611d1f57604052565b90601f8019910116810190811067ffffffffffffffff821117611d1f57604052565b60041115611d9a57565b634e487b7160e01b600052602160045260246000fd5b6004356001600160a01b038116810361038a5790565b6024356001600160a01b038116810361038a5790565b6044356001600160a01b038116810361038a5790565b91908201809211611dff57565b634e487b7160e01b600052601160045260246000fd5b91908203918211611dff57565b8115611e2c570490565b634e487b7160e01b600052601260045260246000fd5b307f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161480611f43575b611f1f5760405160208101907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82527f334838d273155aa64e72dba96b6e08ad1f6a9b77f41ef381199d6bd8b7d8b93860408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815260c0810181811067ffffffffffffffff821117611d1f5760405251902090565b7f000000000000000000000000000000000000000000000000000000000000000090565b507f00000000000000000000000000000000000000000000000000000000000000004614611e74565b60643560ff8116810361038a5790565b6001600160a01b036004358181169081900361038a576024359180831680930361038a5760443590811680910361038a576064359060ff821680920361038a576040519360208501937fcc78d70c3a92f4a3b3950ed2991fce6825158116644c7f4952935372197ef2a2855260408601526060850152608084015260a083015260843560c083015260a43560e083015260c43561010083015260e4356101208301526101043561014083015261012435610160830152610144356101808301526101a0610164358184015282526101c082019167ffffffffffffffff9281811084821117611d1f57604052519020612072611e42565b9160405191602083019361190160f01b85526022840152604283015260428252608082019082821090821117611d1f5760405251902090565b81519190604183036120dc576120d592506020820151906060604084015193015160001a906124bc565b9192909190565b505060009160029190565b6001600160a01b039081169181169082821461213c577f000000000000000000000000000000000000000000000000000000000000000016809214612135571461213057600290565b600190565b5050600090565b604051635c6d7b7360e01b8152600490fd5b51906001600160a01b038216820361038a57565b519065ffffffffffff8216820361038a57565b60405163a9059cbb60e01b60009081526001600160a01b039384166004526024949094529260209060448180855af16001600051148116156121d6575b83604052156121c057505050565b635274afe760e01b835216600482015260249150fd5b60018115166121ec57813b15153d1516166121b2565b833d6000823e3d90fd5b6001600160a01b0390811694937f000000000000000000000000000000000000000000000000000000000000000082169390919061223581868961332c565b7f000000000000000000000000000000000000000000000000000000000000000015612370576040519261226884611d51565b878452826020850195168552604084019162ffffff807f00000000000000000000000000000000000000000000000000000000000000001684528460608701308152608088019242845260a0890194855260c0890195865260e08901968360009b8c8a52816040519c8d63414bf38960e01b9052511660048d0152511660248b015251166044890152511660648701525160848601525160a48501525160c4840152511660e48201526020816101048185875af1918215610edb578092612339575b505090612337919361327a565b565b9091506020823d602011612368575b8161235560209383611d6e565b8101031261014d5750516123373861232a565b3d9150612348565b6040519261237d84611d35565b87845282602085019516855260408401918362ffffff807f000000000000000000000000000000000000000000000000000000000000000016855260608701903082526080880193845260a0880194855260c08801958360009a8b8952816040519b6304e45aaf60e01b8d52511660048c0152511660248a015251166044880152511660648601525160848501525160a4840152511660c482015260208160e48185875af1918215610edb57809261233957505090612337919361327a565b6040516370a0823160e01b81523060048201526001600160a01b039190911691602082602481865afa9182156111d257600092612488575b508161247f57505050565b61233792612175565b9091506020813d6020116124b4575b816124a460209383611d6e565b8101031261038a57519038612474565b3d9150612497565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161253457926020929160ff608095604051948552168484015260408301526060820152600092839182805260015afa15610edb5780516001600160a01b0381161561252b57918190565b50809160019190565b50505060009160039190565b600019828209908281029283808410930392808403931461258f57600160601b918383111561257c570990828211900360a01b910360601c1790565b634e487b7160005260116020526024601cfd5b50505060601c90565b90600019818309818302918280831092039180830392146125f657620f4240908282111561257c577fde8f6cefed634549b62c77574f722e1ac57e23f24d8fd5cb790fb65668c26139940990828211900360fa1b910360061c170290565b5050620f424091500490565b906000198183098183029182808310920391808303921461265f57612710908282111561257c577fbc01a36e2eb1c432ca57a786c226809d495182a9930be0ded288ce703afb7e91940990828211900360fc1b910360041c170290565b505061271091500490565b60001982820990828102928380841093039280840393146126ab5768010000000000000000918383111561257c570990828211900360c01b910360401c1790565b50505060401c90565b90600160801b90600019838309928060801b9283808610950394808603951461274a5784831115612731579082910981600003821680920460028082600302188083028203028083028203028083028203028083028203028083028203028092029003029360018380600003040190848311900302920304170290565b82634e487b71600052156003026011186020526024601cfd5b5050906127579250611e22565b90565b600019828209908281029283808410930392808403931461279657600160801b918383111561257c570990828211900360801b910360801c1790565b50505060801c90565b90600160c01b90600019838309928060c01b9283808610950394808603951461274a5784831115612731579082910981600003821680920460028082600302188083028203028083028203028083028203028083028203028083028203028092029003029360018380600003040190848311900302920304170290565b600019828209908281029283808410930392808403931461285857600160c01b918383111561257c570990828211900360401b910360c01c1790565b50505060c01c90565b519061ffff8216820361038a57565b519063ffffffff8216820361038a57565b5190811515820361038a57565b51908160060b820361038a57565b67ffffffffffffffff8111611d1f5760051b60200190565b8051156128c15760200190565b634e487b7160e01b600052603260045260246000fd5b8051600110156128c15760400190565b604051630b4c774160e11b81526001600160a01b03828116600483015280841660248301527f000000000000000000000000000000000000000000000000000000000000000062ffffff166044830152909290602090849060649082907f0000000000000000000000000000000000000000000000000000000000000000165afa9283156111d257600093613234575b506001600160a01b0383161561322257604051633850c7bd60e01b815260e0816004816001600160a01b0388165afa80156111d257600091829161319e575b50600261ffff8092161061312b5760405163252c09d760e01b8152911660048201526080816024816001600160a01b0388165afa80156111d25760009160009161313d575b501561312b5763ffffffff612a11911642611e15565b927f000000000000000000000000000000000000000000000000000000000000000093633fffffff8560021c161061312b576040519081606081011067ffffffffffffffff606084011117611d1f579060608101604052600281526020810191604036843763ffffffff8616612a86836128b4565b526000612a92836128d7565b52600091829160405194859263883bdbfd60e01b845260248401906020600486015251809152604484019190855b818110613109575050508290039082906001600160a01b03165afa90918282612fdf575b5050612afc5760405163370aafe360e11b8152600490fd5b92600093612b13612b0c826128d7565b51916128b4565b5160060b9060060b0393667fffffffffffff1991667fffffffffffff861383871217612fcb5763ffffffff1660060b9460060b908515612fb75760001992821486841416612fa3575084810560020b94600082129182612f94575b5050612f80575b60009360020b9084821215612f7a57818503915b620d89e88311612f685785906001841615612f56576001600160881b036ffffcb933bd6fad37aa2d162d1a5940015b169360028116612f3a575b60048116612f1e575b60088116612f02575b60108116612ee6575b60208116612eca575b60408116612eae575b60808116612e92575b6101008116612e76575b6102008116612e5a575b6104008116612e3e575b6108008116612e22575b6110008116612e06575b6120008116612dea575b6140008116612dce575b6180008116612db2575b620100008116612d96575b620200008116612d7b575b620400008116612d60575b6208000016612d48575b13612d27575b5063ffffffff8116612d1f5783905b60201c60ff91909116016001600160a01b0316926fffffffffffffffffffffffffffffffff8411612cec575082800292808404811490151715611dff576001600160a01b0390811691161015612ce357906127579161281c565b6127579161279f565b9280612cf79161266a565b92506001600160a01b0390811691161015612d1657906127579161275a565b612757916126b4565b600190612c89565b8115612d34570438612c7a565b634e487b7160e01b85526012600452602485fd5b926b048a170391f7dc42444e8fa20260801c92612c74565b6d2216e584f5fa1ea926041bedfe9890940260801c93612c6a565b936e5d6af8dedb81196699c329225ee6040260801c93612c5f565b936f09aa508b5b7a84e1c677de54f3e99bc90260801c93612c54565b936f31be135f97d08fd981231505542fcfa60260801c93612c49565b936f70d869a156d2a1b890bb3df62baf32f70260801c93612c3f565b936fa9f746462d870fdf8a65dc1f90e061e50260801c93612c35565b936fd097f3bdfd2022b8845ad8f792aa58250260801c93612c2b565b936fe7159475a2c29b7443b29c7fa6e889d90260801c93612c21565b936ff3392b0822b70005940c7a398e4b70f30260801c93612c17565b936ff987a7253ac413176f2b074cf7815e540260801c93612c0d565b936ffcbe86c7900a88aedcffc83b479aa3a40260801c93612c03565b936ffe5dee046a99a2a811c461f1969c30530260801c93612bf9565b936fff2ea16466c96a3843ec78b326b528610260801c93612bf0565b936fff973b41fa98c081472e6896dfb254c00260801c93612be7565b936fffcb9843d60f6159c9db58835c9266440260801c93612bde565b936fffe5caca7e10e4e61c3624eaa0941cd00260801c93612bd5565b936ffff2e50f5f656932ef12357cf3c7fdcc0260801c93612bcc565b936ffff97272373d413259a46990580e213a0260801c93612bc3565b6001600160881b03600160801b612bb8565b60405163c7e13a0160e01b8152600490fd5b81612b89565b92627fffff198114611dff57830192612b75565b0760060b151590503880612b6e565b634e487b7160e01b81526011600452602490fd5b634e487b7160e01b81526012600452602490fd5b634e487b7160e01b82526011600452602482fd5b909192503d908181843e612ff38284611d6e565b604083838101031261014d5782519267ffffffffffffffff8411611af057828101601f858301011215611af057838101519361302e8561289c565b9461303c6040519687611d6e565b80865260208087019160051b8385010101918584018311611989578301602001905b8282106130f15750505060208101519067ffffffffffffffff8211611aec57838101601f838301011215611aec57818101519260208061309d8661289c565b6130aa6040519182611d6e565b868152019460051b8484010101948201851161014d575001602001905b8282106130d957505050903880612ae4565b602080916130e68461214e565b8152019101906130c7565b602080916130fe8461288e565b81520191019061305e565b825163ffffffff16845287965088955060209384019390920191600101612ac0565b60405163370aafe360e11b8152600490fd5b9150506080813d608011613196575b8161315960809383611d6e565b8101031261038a57613190606061316f83612870565b9261317c6020820161288e565b506131896040820161214e565b5001612881565b386129fb565b3d915061314c565b91905060e0823d60e01161321a575b816131ba60e09383611d6e565b8101031261014d576131cb8261214e565b5060208201518060020b0361014d57506131e760408201612861565b61321260c06131f860608501612861565b9361320560808201612861565b5061318960a08201612870565b5090386129b6565b3d91506131ad565b6040516301dbb3ff60e61b8152600490fd5b6020939193813d602011613272575b8161325060209383611d6e565b81010312611af05751906001600160a01b038216820361014d57509138612977565b3d9150613243565b60405163095ea7b360e01b60009081526001600160a01b0380851660045260248290529293919060209060448180885af19060016000511482161561331d575b604052156132c757505050565b6132d1818461340b565b15613303576132e0908361340b565b156132e9575050565b604051635274afe760e01b81529116600482015260249150fd5b604051635274afe760e01b81528284166004820152602490fd5b90843b15153d151616906132ba565b60405163095ea7b360e01b60008181526001600160a01b038086166004819052602488905292969095929390929160209060448180895af1906001600051148216156133fc575b60405215613384575b505050505050565b61338e908461340b565b156133e2579060209160009160405196835260045260245260448180855af16001600051148116156133cc575b83604052156121c05780808061337c565b60018115166121ec57813b15153d1516166133bb565b604051635274afe760e01b81528484166004820152602490fd5b90853b15153d15161690613373565b60405163095ea7b360e01b60009081526001600160a01b0390931660045260248390529192919060208460448180855af1936001815114851615613451575b5050604052565b6001859395151661346c57503b15153d15161691388061344a565b84903d90823e3d90fdfea2646970667358221220a8ccdc9d31132f5d656c3e97f98db1bdf433479fed727452e9a94f57a68d71e864736f6c63430008180033000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3000000000000000000000000caf681a66d020601342297493863e78c959e5cb20000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad730000000000000000000000001f7d7550b1b028f7571e69a784071f0205fd2efa000000000000000000000000a4e441597bdc106a602823aad7557f427f40345a000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000708
0x608080604052600436101561001357600080fd5b600090813560e01c9081631a65893b14611cc2575080632ac1262214611c935780632d2c556514611c4e5780632dd3100014611c095780633644e51514611bee5780634aa4a4fc14611ba95780635d6c499914611b805780636637e38c14611b635780636afdd85014611b1e5780637e2140ef14611af45780638239ed75146103cc57806385a505f51461038f578063b4b01a5514610326578063b88a92b414610309578063bf333f2c146102ed578063c6005893146102a8578063dd1b9c4a14610268578063dff612c014610243578063e229cd7614610226578063e2c1d1d1146101e5578063f6e8bdf3146101505763f973a2091461011357600080fd5b3461014d578060031936011261014d5760206040517fcc78d70c3a92f4a3b3950ed2991fce6825158116644c7f4952935372197ef2a28152f35b80fd5b503461014d5761018036600319011261014d576001600160a01b039081610175611db0565b1633036101d357602091610187611f7c565b8083526002845260408320805460ff19166001179055906101a6611db0565b16817fa6eb7cdc219e1518ced964e9a34e61d68a94e4f1569db3e84256ba981ba527536040519480a38152f35b6040516330cd747160e01b8152600490fd5b503461014d578060031936011261014d57602060405163ffffffff7f0000000000000000000000000000000000000000000000000000000000000708168152f35b503461014d578060031936011261014d5760206040516113888152f35b503461014d5761018036600319011261014d576020610260611f7c565b604051908152f35b503461014d578060031936011261014d57602060405162ffffff7f0000000000000000000000000000000000000000000000000000000000002710168152f35b503461014d578060031936011261014d576040517f000000000000000000000000caf681a66d020601342297493863e78c959e5cb26001600160a01b03168152602090f35b503461014d578060031936011261014d57602060405160648152f35b503461014d578060031936011261014d576020604051610e108152f35b503461014d57604036600319011261014d576004356001600160a01b03808216820361038a57602435908116810361038a57610361916120e7565b60405190600381101561037657602092508152f35b634e487b7160e01b83526021600452602483fd5b600080fd5b503461014d578060031936011261014d5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b503461014d5736600319016102c08112611af0576101801361014d5767ffffffffffffffff9061018435828111611af05761040b903690600401611cdd565b60c0366101a3190112611aec576102643584811161169557610431903690600401611cdd565b92909160027f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005414611ada5760027f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055610489611f7c565b95868652600260205260ff604087205416611ac8578111611ab457604051916104bc601f8301601f191660200184611d6e565b81835236828201116116a45760208284938893836104e097013783010152856120ab565b506104ea81611d90565b1590811591611a96575b50611a84576001600160a01b03610509611db0565b16158015611a6c575b8015611a54575b611a4257600360ff610529611f6c565b1611611a30576113886101443511611a1e5760c43560e4351115611a0c5765ffffffffffff60e43511611a0c5761010435158015611a02575b611927576001600160a01b0361010435116119f05760ff610581611f6c565b1661058b81611d90565b61059481611d90565b6001811480156119dd575b156119bb5761012435156119a9576002905b6105ba81611d90565b148061199f575b61198d576105de6105d0611dc6565b6105d8611ddc565b906120e7565b936101a435916001600160a01b0383168303611989576001600160a01b03610604611dc6565b166001600160a01b038416036119775761022435926001600160a01b03841684036116a457306001600160a01b03851603611965576101c435906001600160a01b038216820361196157610104356001600160a01b0383160361194f576101e4359265ffffffffffff8416840361194b5765ffffffffffff60e4351665ffffffffffff85160361193957610284351561192757608435610284351161191557848852876020526040882054966106bd6102843589611df2565b976101043589116119035760a4356118bf575b5060c43542106118ad5760e435421161189b57858952600160205260408920548015159081611885575b5061187357858952886020528760408a205560016020524260408a20554796610721611db0565b9561072a611dc6565b60405163927da10560e01b81526001600160a01b0389811660048301528083166024830152306044830152919791606090829060649082907f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3165afa908115611868578d908192611810575b50610284356001600160a01b03909116109081156117fd575b506116ca575b50506001600160a01b03610284351193506116a892505050577f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba36001600160a01b03163b156116a457604051631b63c28b60e11b81526001600160a01b039283166004820152306024820152610284358316604482015290821660648201529085908290608490829084907f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3165af1801561169957908591611681575b50508391610284356003871015918261166d5787156115d5575b86946101243580611586575b5061014435611424575b851561141257856102a43511611407575b87936113f35760018914611359575b879081908760028c0361133457505050506108db611dc6565b916000610915826001600160a01b037f000000000000000000000000caf681a66d020601342297493863e78c959e5cb2811690871661332c565b7f0000000000000000000000000000000000000000000000000000000000000000156111de57506040519061094982611d51565b6001600160a01b0384811683527f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad738116602080850191825262ffffff7f00000000000000000000000000000000000000000000000000000000000027108116604080880191825230606089019081524260808a0190815260a08a01988952600060c08b0181815260e08c01828152945163414bf38960e01b81529b518a1660048d01529751891660248c0152935190941660448a0152518616606489015291516084880152945160a4870152915160c48601529051821660e48501529183916101049183917f000000000000000000000000caf681a66d020601342297493863e78c959e5cb2165af180156111d25760009061119e575b610a999150925b6001600160a01b037f000000000000000000000000caf681a66d020601342297493863e78c959e5cb28116911661327a565b8192821561118c576064830283810460640361117857612710610ac3910480948161112457611e15565b92610af78785610ad1611ddc565b7f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad736121f6565b505b60018a03610fdf57506040516370a0823160e01b8152306004820152906020826024817f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad736001600160a01b03165afa8015610fd4578990610fa0575b610b5f9250611e15565b948510610f8e5784606481020460641485151715610f7a57612710606486020494610bdf908690610bda827f000000000000000000000000a4e441597bdc106a602823aad7557f427f40345a7f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad736001600160a01b0316612175565b611e15565b965b610bfa610bec611dc6565b610bf4611db0565b9061243c565b610c2b610c05611db0565b7f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad7361243c565b602460206001600160a01b03610c3f611dc6565b16604051928380926370a0823160e01b82523060048301525afa908115610f2b578291610f48575b50610f3657602460206001600160a01b03610c80611ddc565b16604051928380926370a0823160e01b82523060048301525afa908115610f2b578291610ef9575b50610ee7576040516370a0823160e01b81523060048201526020816024817f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad736001600160a01b03165afa918215610edb5791610ea9575b50610e97574703610e85576020966002906001600160a01b03610d20611db0565b16857fe05bbd37cea5189e100363500e1fb455cb1287068abf68b76255b512c89ac5388b6040518a8152a314610e00575b50507f7fb08f5e950a93a3b88eba4ac327d81cabdf470b33827529e6378da11ed6575684610d7d611db0565b94610dd1610d89611dc6565b91610d92611ddc565b604080516001600160a01b03928316815261028435602082015290810195909552606085019790975260808401529085169590941693819060a0820190565b0390a460017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055604051908152f35b827fb0e901624fd264d4477ece69c5e885ba3f97f8a5e43ba2aef0e4e5f23615d944610e2a611db0565b93610e33611dc6565b610e7b8a610e3f611ddc565b604080516001600160a01b039283168152602081019690965285018b9052606085019790975260808401529085169590941693819060a0820190565b0390a43880610d51565b6040516355fef2db60e01b8152600490fd5b6040516310c39b9d60e21b8152600490fd5b90506020813d602011610ed3575b81610ec460209383611d6e565b8101031261038a575138610cff565b3d9150610eb7565b604051903d90823e3d90fd5b604051638dd38fad60e01b8152600490fd5b90506020813d602011610f23575b81610f1460209383611d6e565b8101031261038a575138610ca8565b3d9150610f07565b6040513d84823e3d90fd5b604051631be2414760e11b8152600490fd5b90506020813d602011610f72575b81610f6360209383611d6e565b8101031261038a575138610c67565b3d9150610f56565b634e487b7160e01b87526011600452602487fd5b60405163bb2875c360e01b8152600490fd5b506020823d602011610fcc575b81610fba60209383611d6e565b8101031261038a57610b5f9151610b55565b3d9150610fad565b6040513d8b823e3d90fd5b959790506001600160a01b03610ff3611ddc565b166020610ffe611db0565b6040516370a0823160e01b81526001600160a01b03909116600482015291829060249082905afa908115610f2b5782916110f2575b5061103f610bec611ddc565b6001600160a01b0361104f611ddc565b1690602061105b611db0565b6040516370a0823160e01b81526001600160a01b03909116600482015292839060249082905afa80156110e75783906110b3575b6110999250611e15565b97881015610be15760405163bb2875c360e01b8152600490fd5b506020823d6020116110df575b816110cd60209383611d6e565b8101031261038a57611099915161108f565b3d91506110c0565b6040513d85823e3d90fd5b90506020813d60201161111c575b8161110d60209383611d6e565b8101031261038a575138611033565b3d9150611100565b610bda827f000000000000000000000000a4e441597bdc106a602823aad7557f427f40345a7f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad736001600160a01b0316612175565b634e487b7160e01b89526011600452602489fd5b60405163210b89a160e21b8152600490fd5b506020813d6020116111ca575b816111b860209383611d6e565b8101031261038a57610a999051610a60565b3d91506111ab565b6040513d6000823e3d90fd5b604051916111eb83611d35565b6001600160a01b0385811684527f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad738116602080860191825262ffffff7f0000000000000000000000000000000000000000000000000000000000002710811660408089019182523060608a0190815260808a0197885260a08a0189815260c08b018a815292516304e45aaf60e01b81529a51881660048c01529551871660248b015291519092166044890152518416606488015293516084870152905160a48601529151811660c4850152839060e490829085907f000000000000000000000000caf681a66d020601342297493863e78c959e5cb2165af1908115610edb5780916112fd575b50610a99915092610a67565b90506020823d60201161132c575b8161131860209383611d6e565b8101031261014d5750610a999051386112f1565b3d915061130b565b611353919295611345979497611dc6565b61134d611ddc565b906121f6565b50610af9565b6040516370a0823160e01b81523060048201529093506020816024817f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad736001600160a01b03165afa9081156113e85788916113b6575b50926108c2565b90506020813d6020116113e0575b816113d160209383611d6e565b8101031261038a5751386113af565b3d91506113c4565b6040513d8a823e3d90fd5b634e487b7160e01b88526021600452602488fd5b6102a43595506108b3565b604051633e41d18160e21b8152600490fd5b836113f3576002890361153b57611439611dc6565b611465847f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad7380936128e7565b906114a062ffffff7f0000000000000000000000000000000000000000000000000000000000002710169261149a8482612598565b90611e15565b6064810281810460641482151715611527576114e293926114cc61149a936127106114db940490611e15565b906114d5611ddc565b906128e7565b9182612598565b6127109061014435820391821161151357906114fd91612602565b86811161150b575b506108a2565b955038611505565b634e487b7160e01b8a52601160045260248afd5b634e487b7160e01b8c52601160045260248cfd5b6115816115528461154a611dc6565b6114d5611ddc565b61149a62ffffff7f00000000000000000000000000000000000000000000000000000000000027101682612598565b6114e2565b909550600093508260028a146115a8575b906115a191612540565b9438610898565b50606483028381046064148415171561117857906115cd6127106115a1930485611e15565b909150611597565b9350506064610284350261028435810460640361165957612710900492611646847f000000000000000000000000a4e441597bdc106a602823aad7557f427f40345a7f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad736001600160a01b0316612175565b6116538461028435611e15565b9061088c565b634e487b7160e01b86526011600452602486fd5b634e487b7160e01b87526021600452602487fd5b61168a90611d0b565b611695578338610872565b8380fd5b6040513d87823e3d90fd5b8580fd5b6040516306dfcc6560e41b815260a06004820152610284356024820152604490fd5b7f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba36001600160a01b03163b156117f9576040516302b67b5760e41b81526001600160a01b03898116600483015296871660248201529516604486015265ffffffffffff908116606486015261020435908116908190036117f55760848501526001600160a01b031660a48401526102443560c484015261010060e4840152610104830181905288918391610124918391908190848401378181018301859052601f01601f19168101030181837f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba36001600160a01b03165af180156117ea576117d7575b80808080806107b5565b6117e390969196611d0b565b94386117cd565b6040513d89823e3d90fd5b8a80fd5b8b80fd5b905065ffffffffffff42911610386107af565b9150506060813d606011611860575b8161182c60609383611d6e565b8101031261185c5761183d8161214e565b611855604061184e60208501612162565b9301612162565b5038610796565b8c80fd5b3d915061181f565b6040513d8f823e3d90fd5b60405163c885103360e01b8152600490fd5b611893915060a43590611df2565b4210386106fa565b604051630407b05b60e31b8152600490fd5b604051636f312cbd60e01b8152600490fd5b6118cc9061010435611e15565b80608435106000146118fe57506084355b61028435106118ec57386106d0565b604051636b7b340160e11b8152600490fd5b6118dd565b6040516350b2c4e160e01b8152600490fd5b6040516333fa08af60e21b8152600490fd5b604051631f2a200560e01b8152600490fd5b604051632f491ad760e11b8152600490fd5b8780fd5b604051633b8ccc8f60e11b8152600490fd5b8680fd5b60405163478df8db60e11b8152600490fd5b6040516336273b4b60e11b8152600490fd5b8480fd5b6040516365851d6560e01b8152600490fd5b5060a435156105c1565b6040516343cca87f60e01b8152600490fd5b6101443515806119d2575b6119a9576002906105b1565b5061012435156119c6565b506119e781611d90565b6003811461059f565b60405163feaa6cdb60e01b8152600490fd5b5060843515610562565b604051632a0c9bb560e11b8152600490fd5b60405163428637bb60e11b8152600490fd5b60405163688c176f60e01b8152600490fd5b60405163d92e233d60e01b8152600490fd5b506001600160a01b03611a65611ddc565b1615610519565b506001600160a01b03611a7d611dc6565b1615610512565b60405163954095ab60e01b8152600490fd5b90506001600160a01b0380611aa9611db0565b1691161415386104f4565b634e487b7160e01b85526041600452602485fd5b6040516374ca404360e01b8152600490fd5b604051633ee5aeb560e01b8152600490fd5b8280fd5b5080fd5b503461014d57602036600319011261014d5760406020916004358152600183522054604051908152f35b503461014d578060031936011261014d576040517f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba36001600160a01b03168152602090f35b503461014d578060031936011261014d5760206040516127108152f35b503461014d57602036600319011261014d57604060209160043581528083522054604051908152f35b503461014d578060031936011261014d576040517f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad736001600160a01b03168152602090f35b503461014d578060031936011261014d576020610260611e42565b503461014d578060031936011261014d576040517f0000000000000000000000001f7d7550b1b028f7571e69a784071f0205fd2efa6001600160a01b03168152602090f35b503461014d578060031936011261014d576040517f000000000000000000000000a4e441597bdc106a602823aad7557f427f40345a6001600160a01b03168152602090f35b503461014d57602036600319011261014d5760ff60406020926004358152600284522054166040519015158152f35b905034611af05781600319360112611af05780603c60209252f35b9181601f8401121561038a5782359167ffffffffffffffff831161038a576020838186019501011161038a57565b67ffffffffffffffff8111611d1f57604052565b634e487b7160e01b600052604160045260246000fd5b60e0810190811067ffffffffffffffff821117611d1f57604052565b610100810190811067ffffffffffffffff821117611d1f57604052565b90601f8019910116810190811067ffffffffffffffff821117611d1f57604052565b60041115611d9a57565b634e487b7160e01b600052602160045260246000fd5b6004356001600160a01b038116810361038a5790565b6024356001600160a01b038116810361038a5790565b6044356001600160a01b038116810361038a5790565b91908201809211611dff57565b634e487b7160e01b600052601160045260246000fd5b91908203918211611dff57565b8115611e2c570490565b634e487b7160e01b600052601260045260246000fd5b307f0000000000000000000000005bf57b8a39fc73e518133e9373ff34d1982c148f6001600160a01b03161480611f43575b611f1f5760405160208101907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82527f334838d273155aa64e72dba96b6e08ad1f6a9b77f41ef381199d6bd8b7d8b93860408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815260c0810181811067ffffffffffffffff821117611d1f5760405251902090565b7f6ff85ae91aee968e13c0e0bb08d7f2ee97371cbaf9f1417f2babe51fb45e885190565b507f00000000000000000000000000000000000000000000000000000000000012374614611e74565b60643560ff8116810361038a5790565b6001600160a01b036004358181169081900361038a576024359180831680930361038a5760443590811680910361038a576064359060ff821680920361038a576040519360208501937fcc78d70c3a92f4a3b3950ed2991fce6825158116644c7f4952935372197ef2a2855260408601526060850152608084015260a083015260843560c083015260a43560e083015260c43561010083015260e4356101208301526101043561014083015261012435610160830152610144356101808301526101a0610164358184015282526101c082019167ffffffffffffffff9281811084821117611d1f57604052519020612072611e42565b9160405191602083019361190160f01b85526022840152604283015260428252608082019082821090821117611d1f5760405251902090565b81519190604183036120dc576120d592506020820151906060604084015193015160001a906124bc565b9192909190565b505060009160029190565b6001600160a01b039081169181169082821461213c577f0000000000000000000000000bd7d308f8e1639fab988df18a8011f41eacad7316809214612135571461213057600290565b600190565b5050600090565b604051635c6d7b7360e01b8152600490fd5b51906001600160a01b038216820361038a57565b519065ffffffffffff8216820361038a57565b60405163a9059cbb60e01b60009081526001600160a01b039384166004526024949094529260209060448180855af16001600051148116156121d6575b83604052156121c057505050565b635274afe760e01b835216600482015260249150fd5b60018115166121ec57813b15153d1516166121b2565b833d6000823e3d90fd5b6001600160a01b0390811694937f000000000000000000000000caf681a66d020601342297493863e78c959e5cb282169390919061223581868961332c565b7f000000000000000000000000000000000000000000000000000000000000000015612370576040519261226884611d51565b878452826020850195168552604084019162ffffff807f00000000000000000000000000000000000000000000000000000000000027101684528460608701308152608088019242845260a0890194855260c0890195865260e08901968360009b8c8a52816040519c8d63414bf38960e01b9052511660048d0152511660248b015251166044890152511660648701525160848601525160a48501525160c4840152511660e48201526020816101048185875af1918215610edb578092612339575b505090612337919361327a565b565b9091506020823d602011612368575b8161235560209383611d6e565b8101031261014d5750516123373861232a565b3d9150612348565b6040519261237d84611d35565b87845282602085019516855260408401918362ffffff807f000000000000000000000000000000000000000000000000000000000000271016855260608701903082526080880193845260a0880194855260c08801958360009a8b8952816040519b6304e45aaf60e01b8d52511660048c0152511660248a015251166044880152511660648601525160848501525160a4840152511660c482015260208160e48185875af1918215610edb57809261233957505090612337919361327a565b6040516370a0823160e01b81523060048201526001600160a01b039190911691602082602481865afa9182156111d257600092612488575b508161247f57505050565b61233792612175565b9091506020813d6020116124b4575b816124a460209383611d6e565b8101031261038a57519038612474565b3d9150612497565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161253457926020929160ff608095604051948552168484015260408301526060820152600092839182805260015afa15610edb5780516001600160a01b0381161561252b57918190565b50809160019190565b50505060009160039190565b600019828209908281029283808410930392808403931461258f57600160601b918383111561257c570990828211900360a01b910360601c1790565b634e487b7160005260116020526024601cfd5b50505060601c90565b90600019818309818302918280831092039180830392146125f657620f4240908282111561257c577fde8f6cefed634549b62c77574f722e1ac57e23f24d8fd5cb790fb65668c26139940990828211900360fa1b910360061c170290565b5050620f424091500490565b906000198183098183029182808310920391808303921461265f57612710908282111561257c577fbc01a36e2eb1c432ca57a786c226809d495182a9930be0ded288ce703afb7e91940990828211900360fc1b910360041c170290565b505061271091500490565b60001982820990828102928380841093039280840393146126ab5768010000000000000000918383111561257c570990828211900360c01b910360401c1790565b50505060401c90565b90600160801b90600019838309928060801b9283808610950394808603951461274a5784831115612731579082910981600003821680920460028082600302188083028203028083028203028083028203028083028203028083028203028092029003029360018380600003040190848311900302920304170290565b82634e487b71600052156003026011186020526024601cfd5b5050906127579250611e22565b90565b600019828209908281029283808410930392808403931461279657600160801b918383111561257c570990828211900360801b910360801c1790565b50505060801c90565b90600160c01b90600019838309928060c01b9283808610950394808603951461274a5784831115612731579082910981600003821680920460028082600302188083028203028083028203028083028203028083028203028083028203028092029003029360018380600003040190848311900302920304170290565b600019828209908281029283808410930392808403931461285857600160c01b918383111561257c570990828211900360401b910360c01c1790565b50505060c01c90565b519061ffff8216820361038a57565b519063ffffffff8216820361038a57565b5190811515820361038a57565b51908160060b820361038a57565b67ffffffffffffffff8111611d1f5760051b60200190565b8051156128c15760200190565b634e487b7160e01b600052603260045260246000fd5b8051600110156128c15760400190565b604051630b4c774160e11b81526001600160a01b03828116600483015280841660248301527f000000000000000000000000000000000000000000000000000000000000271062ffffff166044830152909290602090849060649082907f0000000000000000000000001f7d7550b1b028f7571e69a784071f0205fd2efa165afa9283156111d257600093613234575b506001600160a01b0383161561322257604051633850c7bd60e01b815260e0816004816001600160a01b0388165afa80156111d257600091829161319e575b50600261ffff8092161061312b5760405163252c09d760e01b8152911660048201526080816024816001600160a01b0388165afa80156111d25760009160009161313d575b501561312b5763ffffffff612a11911642611e15565b927f000000000000000000000000000000000000000000000000000000000000070893633fffffff8560021c161061312b576040519081606081011067ffffffffffffffff606084011117611d1f579060608101604052600281526020810191604036843763ffffffff8616612a86836128b4565b526000612a92836128d7565b52600091829160405194859263883bdbfd60e01b845260248401906020600486015251809152604484019190855b818110613109575050508290039082906001600160a01b03165afa90918282612fdf575b5050612afc5760405163370aafe360e11b8152600490fd5b92600093612b13612b0c826128d7565b51916128b4565b5160060b9060060b0393667fffffffffffff1991667fffffffffffff861383871217612fcb5763ffffffff1660060b9460060b908515612fb75760001992821486841416612fa3575084810560020b94600082129182612f94575b5050612f80575b60009360020b9084821215612f7a57818503915b620d89e88311612f685785906001841615612f56576001600160881b036ffffcb933bd6fad37aa2d162d1a5940015b169360028116612f3a575b60048116612f1e575b60088116612f02575b60108116612ee6575b60208116612eca575b60408116612eae575b60808116612e92575b6101008116612e76575b6102008116612e5a575b6104008116612e3e575b6108008116612e22575b6110008116612e06575b6120008116612dea575b6140008116612dce575b6180008116612db2575b620100008116612d96575b620200008116612d7b575b620400008116612d60575b6208000016612d48575b13612d27575b5063ffffffff8116612d1f5783905b60201c60ff91909116016001600160a01b0316926fffffffffffffffffffffffffffffffff8411612cec575082800292808404811490151715611dff576001600160a01b0390811691161015612ce357906127579161281c565b6127579161279f565b9280612cf79161266a565b92506001600160a01b0390811691161015612d1657906127579161275a565b612757916126b4565b600190612c89565b8115612d34570438612c7a565b634e487b7160e01b85526012600452602485fd5b926b048a170391f7dc42444e8fa20260801c92612c74565b6d2216e584f5fa1ea926041bedfe9890940260801c93612c6a565b936e5d6af8dedb81196699c329225ee6040260801c93612c5f565b936f09aa508b5b7a84e1c677de54f3e99bc90260801c93612c54565b936f31be135f97d08fd981231505542fcfa60260801c93612c49565b936f70d869a156d2a1b890bb3df62baf32f70260801c93612c3f565b936fa9f746462d870fdf8a65dc1f90e061e50260801c93612c35565b936fd097f3bdfd2022b8845ad8f792aa58250260801c93612c2b565b936fe7159475a2c29b7443b29c7fa6e889d90260801c93612c21565b936ff3392b0822b70005940c7a398e4b70f30260801c93612c17565b936ff987a7253ac413176f2b074cf7815e540260801c93612c0d565b936ffcbe86c7900a88aedcffc83b479aa3a40260801c93612c03565b936ffe5dee046a99a2a811c461f1969c30530260801c93612bf9565b936fff2ea16466c96a3843ec78b326b528610260801c93612bf0565b936fff973b41fa98c081472e6896dfb254c00260801c93612be7565b936fffcb9843d60f6159c9db58835c9266440260801c93612bde565b936fffe5caca7e10e4e61c3624eaa0941cd00260801c93612bd5565b936ffff2e50f5f656932ef12357cf3c7fdcc0260801c93612bcc565b936ffff97272373d413259a46990580e213a0260801c93612bc3565b6001600160881b03600160801b612bb8565b60405163c7e13a0160e01b8152600490fd5b81612b89565b92627fffff198114611dff57830192612b75565b0760060b151590503880612b6e565b634e487b7160e01b81526011600452602490fd5b634e487b7160e01b81526012600452602490fd5b634e487b7160e01b82526011600452602482fd5b909192503d908181843e612ff38284611d6e565b604083838101031261014d5782519267ffffffffffffffff8411611af057828101601f858301011215611af057838101519361302e8561289c565b9461303c6040519687611d6e565b80865260208087019160051b8385010101918584018311611989578301602001905b8282106130f15750505060208101519067ffffffffffffffff8211611aec57838101601f838301011215611aec57818101519260208061309d8661289c565b6130aa6040519182611d6e565b868152019460051b8484010101948201851161014d575001602001905b8282106130d957505050903880612ae4565b602080916130e68461214e565b8152019101906130c7565b602080916130fe8461288e565b81520191019061305e565b825163ffffffff16845287965088955060209384019390920191600101612ac0565b60405163370aafe360e11b8152600490fd5b9150506080813d608011613196575b8161315960809383611d6e565b8101031261038a57613190606061316f83612870565b9261317c6020820161288e565b506131896040820161214e565b5001612881565b386129fb565b3d915061314c565b91905060e0823d60e01161321a575b816131ba60e09383611d6e565b8101031261014d576131cb8261214e565b5060208201518060020b0361014d57506131e760408201612861565b61321260c06131f860608501612861565b9361320560808201612861565b5061318960a08201612870565b5090386129b6565b3d91506131ad565b6040516301dbb3ff60e61b8152600490fd5b6020939193813d602011613272575b8161325060209383611d6e565b81010312611af05751906001600160a01b038216820361014d57509138612977565b3d9150613243565b60405163095ea7b360e01b60009081526001600160a01b0380851660045260248290529293919060209060448180885af19060016000511482161561331d575b604052156132c757505050565b6132d1818461340b565b15613303576132e0908361340b565b156132e9575050565b604051635274afe760e01b81529116600482015260249150fd5b604051635274afe760e01b81528284166004820152602490fd5b90843b15153d151616906132ba565b60405163095ea7b360e01b60008181526001600160a01b038086166004819052602488905292969095929390929160209060448180895af1906001600051148216156133fc575b60405215613384575b505050505050565b61338e908461340b565b156133e2579060209160009160405196835260045260245260448180855af16001600051148116156133cc575b83604052156121c05780808061337c565b60018115166121ec57813b15153d1516166133bb565b604051635274afe760e01b81528484166004820152602490fd5b90853b15153d15161690613373565b60405163095ea7b360e01b60009081526001600160a01b0390931660045260248390529192919060208460448180855af1936001815114851615613451575b5050604052565b6001859395151661346c57503b15153d15161691388061344a565b84903d90823e3d90fdfea2646970667358221220a8ccdc9d31132f5d656c3e97f98db1bdf433479fed727452e9a94f57a68d71e864736f6c63430008180033
| Token | Symbol | Balance | Price | Value |
|---|---|---|---|---|
| DIH | 1 | $0.0000102 | $0 |
| Block | Age | Parent Transaction Hash | Type | Method | From | To | Value | |
|---|---|---|---|---|---|---|---|---|
| no internal transactions found for this address yet (traced blocks + on-demand) | ||||||||
| Type | Age | Block | Details |
|---|---|---|---|
| no cross-chain L1↔L2 transactions for this address | |||
| Transaction Hash | Method | Block | Age | From | To | Type | Item | ||
|---|---|---|---|---|---|---|---|---|---|
| no NFT transfers for this address yet | |||||||||
| Txn Hash | Age | Event | Topics / Data |
|---|---|---|---|
| 0xdc0f11…9de1f8 | 29 days agoSun, 19 Jul 2026 13:31:37 UTC | 0x7fb08f…5756 | [0] 0x04e2b8336598…77661456 [1] 0x000000000000…7a9b0670 [2] 0x000000000000…dc7b22ad data: 0x000000000000000000…ffa67e12 |
| 0xdc0f11…9de1f8 | 29 days agoSun, 19 Jul 2026 13:31:37 UTC | 0xe05bbd…c538 | [0] 0x04e2b8336598…77661456 [1] 0x000000000000…7a9b0670 data: 0x000000000000000000…54d937af |
| 0xf85e92…4424f5 | 29 days agoSun, 19 Jul 2026 13:31:25 UTC | 0x7fb08f…5756 | [0] 0x04e2b8336598…77661456 [1] 0x000000000000…7a9b0670 [2] 0x000000000000…dc7b22ad data: 0x000000000000000000…ffd33f09 |
| 0xf85e92…4424f5 | 29 days agoSun, 19 Jul 2026 13:31:25 UTC | 0xe05bbd…c538 | [0] 0x04e2b8336598…77661456 [1] 0x000000000000…7a9b0670 data: 0x000000000000000000…55bc9f7f |
| 0xd46ee9…b18568 | 29 days agoSun, 19 Jul 2026 13:31:23 UTC | 0x7fb08f…5756 | [0] 0x24ed22d2b3f2…d544a5e0 [1] 0x000000000000…7a9b0670 [2] 0x000000000000…1eacad73 data: 0x000000000000000000…20f48000 |
| 0xd46ee9…b18568 | 29 days agoSun, 19 Jul 2026 13:31:23 UTC | 0xe05bbd…c538 | [0] 0x24ed22d2b3f2…d544a5e0 [1] 0x000000000000…7a9b0670 data: 0x000000000000000000…a94a2000 |
| 0xe11cfd…6a46c1 | 29 days agoSun, 19 Jul 2026 03:06:32 UTC | 0x7fb08f…5756 | [0] 0xc010cbd49b81…44b1280e [1] 0x000000000000…7a9b0670 [2] 0x000000000000…1eacad73 data: 0x000000000000000000…41e90000 |
| 0xe11cfd…6a46c1 | 29 days agoSun, 19 Jul 2026 03:06:32 UTC | 0xe05bbd…c538 | [0] 0xc010cbd49b81…44b1280e [1] 0x000000000000…7a9b0670 data: 0x000000000000000000…a94a2000 |
| 0xe414a7…d85c4e | 29 days agoSun, 19 Jul 2026 03:01:17 UTC | 0x7fb08f…5756 | [0] 0xc010cbd49b81…44b1280e [1] 0x000000000000…7a9b0670 [2] 0x000000000000…1eacad73 data: 0x000000000000000000…20f48000 |
| 0xe414a7…d85c4e | 29 days agoSun, 19 Jul 2026 03:01:17 UTC | 0xe05bbd…c538 | [0] 0xc010cbd49b81…44b1280e [1] 0x000000000000…7a9b0670 data: 0x000000000000000000…a94a2000 |
| 0x33e73b…d27289 | 29 days agoSun, 19 Jul 2026 02:40:03 UTC | 0x7fb08f…5756 | [0] 0xf43fe0a9f73b…4f6f28f3 [1] 0x000000000000…7a9b0670 [2] 0x000000000000…dc7b22ad data: 0x000000000000000000…44c024b0 |
| 0x33e73b…d27289 | 29 days agoSun, 19 Jul 2026 02:40:03 UTC | 0xe05bbd…c538 | [0] 0xf43fe0a9f73b…4f6f28f3 [1] 0x000000000000…7a9b0670 data: 0x000000000000000000…6e53b4ca |
| 0x1f5434…5e7658 | 29 days agoSun, 19 Jul 2026 02:39:02 UTC | 0x7fb08f…5756 | [0] 0xf43fe0a9f73b…4f6f28f3 [1] 0x000000000000…7a9b0670 [2] 0x000000000000…dc7b22ad data: 0x000000000000000000…22601258 |
| 0x1f5434…5e7658 | 29 days agoSun, 19 Jul 2026 02:39:02 UTC | 0xe05bbd…c538 | [0] 0xf43fe0a9f73b…4f6f28f3 [1] 0x000000000000…7a9b0670 data: 0x000000000000000000…b0b776cf |
| 0x7d811b…762637 | 29 days agoSun, 19 Jul 2026 02:36:33 UTC | 0x7fb08f…5756 | [0] 0x1e95c9ee333b…d4492c21 [1] 0x000000000000…7a9b0670 [2] 0x000000000000…1eacad73 data: 0x000000000000000000…41e90000 |
| 0x7d811b…762637 | 29 days agoSun, 19 Jul 2026 02:36:33 UTC | 0xe05bbd…c538 | [0] 0x1e95c9ee333b…d4492c21 [1] 0x000000000000…7a9b0670 data: 0x000000000000000000…52944000 |
| 0x3a34b6…558b6a | 30 days agoFri, 17 Jul 2026 20:04:20 UTC | 0x7fb08f…5756 | [0] 0x24d89ed1a992…927564b6 [1] 0x000000000000…81184562 [2] 0x000000000000…1eacad73 data: 0x000000000000000000…41e90000 |
| 0x3a34b6…558b6a | 30 days agoFri, 17 Jul 2026 20:04:20 UTC | 0xe05bbd…c538 | [0] 0x24d89ed1a992…927564b6 [1] 0x000000000000…81184562 data: 0x000000000000000000…52944000 |
| 0xa9c0fd…207fe9 | 30 days agoFri, 17 Jul 2026 20:00:05 UTC | 0x7fb08f…5756 | [0] 0x5c73ec035ec5…6961968d [1] 0x000000000000…81184562 [2] 0x000000000000…1eacad73 data: 0x000000000000000000…83e3f514 |
| 0xa9c0fd…207fe9 | 30 days agoFri, 17 Jul 2026 20:00:05 UTC | 0xe05bbd…c538 | [0] 0x5c73ec035ec5…6961968d [1] 0x000000000000…81184562 data: 0x000000000000000000…cc2a9eb3 |
| 0x889c38…cc8bda | 30 days agoFri, 17 Jul 2026 19:59:06 UTC | 0x7fb08f…5756 | [0] 0x5c73ec035ec5…6961968d [1] 0x000000000000…81184562 [2] 0x000000000000…1eacad73 data: 0x000000000000000000…c33df6e6 |
| 0x889c38…cc8bda | 30 days agoFri, 17 Jul 2026 19:59:06 UTC | 0xe05bbd…c538 | [0] 0x5c73ec035ec5…6961968d [1] 0x000000000000…81184562 data: 0x000000000000000000…cc2a9eb3 |
| 0x17e84f…52c8b9 | 30 days agoFri, 17 Jul 2026 19:58:21 UTC | 0x7fb08f…5756 | [0] 0x5c73ec035ec5…6961968d [1] 0x000000000000…81184562 [2] 0x000000000000…1eacad73 data: 0x000000000000000000…0297f8b8 |
| 0x17e84f…52c8b9 | 30 days agoFri, 17 Jul 2026 19:58:21 UTC | 0xe05bbd…c538 | [0] 0x5c73ec035ec5…6961968d [1] 0x000000000000…81184562 data: 0x000000000000000000…cc2a9eb3 |
| 0xa305f1…cf7be6 | 30 days agoFri, 17 Jul 2026 19:57:36 UTC | 0x7fb08f…5756 | [0] 0x5c73ec035ec5…6961968d [1] 0x000000000000…81184562 [2] 0x000000000000…1eacad73 data: 0x000000000000000000…41f1fa8a |
| Transaction Hash | Method ? | Block | Age | From | To | Amount | Txn Fee ? | ||
|---|---|---|---|---|---|---|---|---|---|
| 0xdc0f11…9de1f8 | execute | 13,878,363 | 29 days agoSun, 19 Jul 2026 13:31:37 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001166 | |
| 0xf85e92…4424f5 | execute | 13,878,247 | 29 days agoSun, 19 Jul 2026 13:31:25 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001338 | |
| 0xd46ee9…b18568 | execute | 13,878,225 | 29 days agoSun, 19 Jul 2026 13:31:23 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001408 | |
| 0xe11cfd…6a46c1 | execute | 13,505,251 | 29 days agoSun, 19 Jul 2026 03:06:32 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001583 | |
| 0xe414a7…d85c4e | execute | 13,502,114 | 29 days agoSun, 19 Jul 2026 03:01:17 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001925 | |
| 0x33e73b…d27289 | execute | 13,489,430 | 29 days agoSun, 19 Jul 2026 02:40:03 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001354 | |
| !0x0d4ab1…f676c7 | execute | 13,488,981 | 29 days agoSun, 19 Jul 2026 02:39:18 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001672 | |
| 0x1f5434…5e7658 | execute | 13,488,825 | 29 days agoSun, 19 Jul 2026 02:39:02 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001690 | |
| 0x7d811b…762637 | execute | 13,487,334 | 29 days agoSun, 19 Jul 2026 02:36:33 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001739 | |
| 0x3a34b6…558b6a | execute | 12,392,056 | 30 days agoFri, 17 Jul 2026 20:04:20 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001926 | |
| 0xa9c0fd…207fe9 | execute | 12,389,516 | 30 days agoFri, 17 Jul 2026 20:00:05 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001589 | |
| 0x889c38…cc8bda | execute | 12,388,922 | 30 days agoFri, 17 Jul 2026 19:59:06 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001626 | |
| 0x17e84f…52c8b9 | execute | 12,388,473 | 30 days agoFri, 17 Jul 2026 19:58:21 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001631 | |
| 0xa305f1…cf7be6 | execute | 12,388,025 | 30 days agoFri, 17 Jul 2026 19:57:36 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001589 | |
| 0xb8a0f0…be8b0c | execute | 12,387,578 | 30 days agoFri, 17 Jul 2026 19:56:51 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001631 | |
| 0x8402c5…3d3de2 | execute | 12,387,134 | 30 days agoFri, 17 Jul 2026 19:56:06 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001788 | |
| 0x90495e…417139 | execute | 12,386,974 | 30 days agoFri, 17 Jul 2026 19:55:50 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00002262 | |
| 0x3fc4a5…a263a8 | execute | 11,472,133 | 32 days agoThu, 16 Jul 2026 18:26:57 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001803 | |
| 0xc684b2…1b2143 | execute | 11,471,494 | 32 days agoThu, 16 Jul 2026 18:25:53 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00002109 | |
| 0x63544e…29550d | execute | 11,405,368 | 32 days agoThu, 16 Jul 2026 16:35:35 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001708 | |
| 0x3ab236…0f4df0 | execute | 10,825,206 | 32 days agoThu, 16 Jul 2026 00:27:31 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001434 | |
| 0x8fe82d…7d943d | execute | 10,824,808 | 32 days agoThu, 16 Jul 2026 00:26:51 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001469 | |
| 0x92f424…2ea7c0 | execute | 10,824,409 | 32 days agoThu, 16 Jul 2026 00:26:11 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001474 | |
| 0xcb53fd…6428ff | execute | 10,824,011 | 32 days agoThu, 16 Jul 2026 00:25:31 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001473 | |
| 0xce99bb…c3c0f1 | execute | 10,823,614 | 32 days agoThu, 16 Jul 2026 00:24:51 UTC | 0x73de…5238 | IN | NasdankOrderRouterV2 | $0.000 ETH | 0.00001473 |
| Transaction Hash | Method | Block | Age | From | To | Amount | Token | ||
|---|---|---|---|---|---|---|---|---|---|
| 0xbfbf7183…65c248 | Transfer | 19,828,151 | 22 days agoSun, 26 Jul 2026 11:17:20 UTC | 0xcaf7…5d11 | IN | 0x5bf5…148f | $0.001 DIH | ||
| 0xdc0f1156…9de1f8 | Transfer | 13,878,363 | 29 days agoSun, 19 Jul 2026 13:31:37 UTC | 0x5bf5…148f | OUT | 0x3013…0670 | 0.000032 WETH | WETH (WETH) | |
| 0xdc0f1156…9de1f8 | Transfer | 13,878,363 | 29 days agoSun, 19 Jul 2026 13:31:37 UTC | 0x5bf5…148f | OUT | 0xa4e4…345a | 0.000000 WETH | WETH (WETH) | |
| 0xdc0f1156…9de1f8 | Transfer | 13,878,363 | 29 days agoSun, 19 Jul 2026 13:31:37 UTC | 0x5bf5…148f | OUT | 0x1738…5a95 | 299,318.216704 HORNEYDOG | Horney dog (HORNEYDOG) | |
| 0xdc0f1156…9de1f8 | Transfer | 13,878,363 | 29 days agoSun, 19 Jul 2026 13:31:37 UTC | 0x1738…5a95 | IN | 0x5bf5…148f | 0.000032 WETH | WETH (WETH) | |
| 0xdc0f1156…9de1f8 | Transfer | 13,878,363 | 29 days agoSun, 19 Jul 2026 13:31:37 UTC | 0x3013…0670 | IN | 0x5bf5…148f | 299,318.216704 HORNEYDOG | Horney dog (HORNEYDOG) | |
| 0xf85e925f…4424f5 | Transfer | 13,878,247 | 29 days agoSun, 19 Jul 2026 13:31:25 UTC | 0x5bf5…148f | OUT | 0x3013…0670 | 0.000032 WETH | WETH (WETH) | |
| 0xf85e925f…4424f5 | Transfer | 13,878,247 | 29 days agoSun, 19 Jul 2026 13:31:25 UTC | 0x5bf5…148f | OUT | 0xa4e4…345a | 0.000000 WETH | WETH (WETH) | |
| 0xf85e925f…4424f5 | Transfer | 13,878,247 | 29 days agoSun, 19 Jul 2026 13:31:25 UTC | 0x5bf5…148f | OUT | 0x1738…5a95 | 299,318.216704 HORNEYDOG | Horney dog (HORNEYDOG) | |
| 0xf85e925f…4424f5 | Transfer | 13,878,247 | 29 days agoSun, 19 Jul 2026 13:31:25 UTC | 0x1738…5a95 | IN | 0x5bf5…148f | 0.000032 WETH | WETH (WETH) | |
| 0xf85e925f…4424f5 | Transfer | 13,878,247 | 29 days agoSun, 19 Jul 2026 13:31:25 UTC | 0x3013…0670 | IN | 0x5bf5…148f | 299,318.216704 HORNEYDOG | Horney dog (HORNEYDOG) | |
| 0xd46ee9ae…b18568 | Transfer | 13,878,225 | 29 days agoSun, 19 Jul 2026 13:31:23 UTC | 0x5bf5…148f | OUT | 0x3013…0670 | 1,795,408.155173 HORNEYDOG | Horney dog (HORNEYDOG) | |
| 0xd46ee9ae…b18568 | Transfer | 13,878,225 | 29 days agoSun, 19 Jul 2026 13:31:23 UTC | 0x5bf5…148f | OUT | 0x1738…5a95 | 0.000198 WETH | WETH (WETH) | |
| 0xd46ee9ae…b18568 | Transfer | 13,878,225 | 29 days agoSun, 19 Jul 2026 13:31:23 UTC | 0x1738…5a95 | IN | 0x5bf5…148f | 1,795,408.155173 HORNEYDOG | Horney dog (HORNEYDOG) | |
| 0xd46ee9ae…b18568 | Transfer | 13,878,225 | 29 days agoSun, 19 Jul 2026 13:31:23 UTC | 0x5bf5…148f | OUT | 0xa4e4…345a | 0.000002 WETH | WETH (WETH) | |
| 0xd46ee9ae…b18568 | Transfer | 13,878,225 | 29 days agoSun, 19 Jul 2026 13:31:23 UTC | 0x3013…0670 | IN | 0x5bf5…148f | 0.0002 WETH | WETH (WETH) | |
| 0xe11cfd9d…6a46c1 | Transfer | 13,505,251 | 29 days agoSun, 19 Jul 2026 03:06:32 UTC | 0x5bf5…148f | OUT | 0x3013…0670 | 1,789,072.626720 HORNEYDOG | Horney dog (HORNEYDOG) | |
| 0xe11cfd9d…6a46c1 | Transfer | 13,505,251 | 29 days agoSun, 19 Jul 2026 03:06:32 UTC | 0x5bf5…148f | OUT | 0x1738…5a95 | 0.000198 WETH | WETH (WETH) | |
| 0xe11cfd9d…6a46c1 | Transfer | 13,505,251 | 29 days agoSun, 19 Jul 2026 03:06:32 UTC | 0x1738…5a95 | IN | 0x5bf5…148f | 1,789,072.626720 HORNEYDOG | Horney dog (HORNEYDOG) | |
| 0xe11cfd9d…6a46c1 | Transfer | 13,505,251 | 29 days agoSun, 19 Jul 2026 03:06:32 UTC | 0x5bf5…148f | OUT | 0xa4e4…345a | 0.000002 WETH | WETH (WETH) | |
| 0xe11cfd9d…6a46c1 | Transfer | 13,505,251 | 29 days agoSun, 19 Jul 2026 03:06:32 UTC | 0x3013…0670 | IN | 0x5bf5…148f | 0.0002 WETH | WETH (WETH) | |
| 0xe414a79b…d85c4e | Transfer | 13,502,114 | 29 days agoSun, 19 Jul 2026 03:01:17 UTC | 0x5bf5…148f | OUT | 0x3013…0670 | 1,789,571.121314 HORNEYDOG | Horney dog (HORNEYDOG) | |
| 0xe414a79b…d85c4e | Transfer | 13,502,114 | 29 days agoSun, 19 Jul 2026 03:01:17 UTC | 0x5bf5…148f | OUT | 0x1738…5a95 | 0.000198 WETH | WETH (WETH) | |
| 0xe414a79b…d85c4e | Transfer | 13,502,114 | 29 days agoSun, 19 Jul 2026 03:01:17 UTC | 0x1738…5a95 | IN | 0x5bf5…148f | 1,789,571.121314 HORNEYDOG | Horney dog (HORNEYDOG) | |
| 0xe414a79b…d85c4e | Transfer | 13,502,114 | 29 days agoSun, 19 Jul 2026 03:01:17 UTC | 0x5bf5…148f | OUT | 0xa4e4…345a | 0.000002 WETH | WETH (WETH) |