AlpsRouter

0xee147478b8910f426fdd5ce763f75ee9f3dd842e

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
11,833 bytes
ABI entries
56
19 read · 10 write
License
none

Contract information

Address
0xee147478b8910f426fdd5ce763f75ee9f3dd842e
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0xdd3323a064…073bd2ba35

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (19)

BPS()uint256
MAX_RETRO_FEE()uint256
MAX_SLIPPAGE_BPS()uint256
OBS_CARDINALITY()uint16
ORDER_TTL()uint64
PLATFORM_FEE_BPS()uint256
TWAP_MAX()uint32
TWAP_MIN()uint32
factory()address
feeSink()address
keeper()address
mintedBy(uint256)address
npm()address
orders(uint256)int24, int24, uint16, uint64, address
owner()address
pendingOwner()address
retroEnableFeeWei()uint256
swapRouter()address
weth()address

Events (9)

KeeperChangedOrderCancelledOrderExecutedOrderRegisteredOwnershipTransferStartedOwnershipTransferredRetroEnableFeeChangedRetroEnabledZapMinted

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "npm_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "swapRouter_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "factory_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "weth_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "feeSink_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "keeper_",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "BadParams",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "FeeTooHigh",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NativeMismatch",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NoOrder",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotApproved",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotKeeper",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotPositionOwner",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotPremium",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotTriggered",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OrderExpired",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "OwnableInvalidOwner",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "OwnableUnauthorizedAccount",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ReentrancyGuardReentrantCall",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "SafeERC20FailedOperation",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "StaleOracle",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "T",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "keeper",
        "type": "address"
      }
    ],
    "name": "KeeperChanged",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "OrderCancelled",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "int24",
        "name": "spotTick",
        "type": "int24"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount0",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount1",
        "type": "uint256"
      }
    ],
    "name": "OrderExecuted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "int24",
        "name": "tickBelow",
        "type": "int24"
      },
      {
        "indexed": false,
        "internalType": "int24",
        "name": "tickAbove",
        "type": "int24"
      },
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "slippageBps",
        "type": "uint16"
      },
      {
        "indexed": false,
        "internalType": "uint64",
        "name": "expiry",
        "type": "uint64"
      }
    ],
    "name": "OrderRegistered",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousOwner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "OwnershipTransferStarted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousOwner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "OwnershipTransferred",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "feeWei",
        "type": "uint256"
      }
    ],
    "name": "RetroEnableFeeChanged",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "feePaid",
        "type": "uint256"
      }
    ],
    "name": "RetroEnabled",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "tokenIn",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amountIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "feeTaken",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint128",
        "name": "liquidity",
        "type": "uint128"
      }
    ],
    "name": "ZapMinted",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "BPS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_RETRO_FEE",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_SLIPPAGE_BPS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "OBS_CARDINALITY",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "ORDER_TTL",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "PLATFORM_FEE_BPS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "TWAP_MAX",
    "outputs": [
      {
        "internalType": "uint32",
        "name": "",
        "type": "uint32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "TWAP_MIN",
    "outputs": [
      {
        "internalType": "uint32",
        "name": "",
        "type": "uint32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "acceptOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "cancelOrder",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "enableExisting",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "executeOrder",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "factory",
    "outputs": [
      {
        "internalType": "contract IUniswapV3Factory",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "feeSink",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "keeper",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "mintedBy",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "npm",
    "outputs": [
      {
        "internalType": "contract INonfungiblePositionManager",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "orders",
    "outputs": [
      {
        "internalType": "int24",
        "name": "tickBelow",
        "type": "int24"
      },
      {
        "internalType": "int24",
        "name": "tickAbove",
        "type": "int24"
      },
      {
        "internalType": "uint16",
        "name": "slippageBps",
        "type": "uint16"
      },
      {
        "internalType": "uint64",
        "name": "expiry",
        "type": "uint64"
      },
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "owner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pendingOwner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "int24",
        "name": "tickBelow",
        "type": "int24"
      },
      {
        "internalType": "int24",
        "name": "tickAbove",
        "type": "int24"
      },
      {
        "internalType": "uint16",
        "name": "slippageBps",
        "type": "uint16"
      }
    ],
    "name": "registerOrder",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "renounceOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "retroEnableFeeWei",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "keeper_",
        "type": "address"
      }
    ],
    "name": "setKeeper",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "feeWei",
        "type": "uint256"
      }
    ],
    "name": "setRetroEnableFee",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "swapRouter",
    "outputs": [
      {
        "internalType": "contract ISwapRouter02",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "transferOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "weth",
    "outputs": [
      {
        "internalType": "contract IWETH",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "tokenIn",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "amountIn",
            "type": "uint256"
          },
          {
            "internalType": "address",
            "name": "token0",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "token1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "poolFee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickLower",
            "type": "int24"
          },
          {
            "internalType": "int24",
            "name": "tickUpper",
            "type": "int24"
          },
          {
            "internalType": "uint256",
            "name": "swapAmountIn",
            "type": "uint256"
          },
          {
            "internalType": "uint24",
            "name": "swapPoolFee",
            "type": "uint24"
          },
          {
            "internalType": "uint256",
            "name": "swapMinOut",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "amount0Min",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "amount1Min",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "deadline",
            "type": "uint256"
          }
        ],
        "internalType": "struct AlpsRouter.ZapParams",
        "name": "p",
        "type": "tuple"
      }
    ],
    "name": "zapMint",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "uint128",
        "name": "liquidity",
        "type": "uint128"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20 as OZIERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {TickMath} from "@uniswap/v3-core/contracts/libraries/TickMath.sol";
import {LiquidityAmounts} from "@uniswap/v3-periphery/contracts/libraries/LiquidityAmounts.sol";
import {
    IERC20,
    IWETH,
    INonfungiblePositionManager,
    ISwapRouter02,
    IUniswapV3Factory,
    IUniswapV3Pool
} from "./interfaces/IExternal.sol";

/// @title AlpsRouter — zap mints + TP/SL automation for Uniswap v3 on Robinhood Chain
///
/// The basic Alps terminal talks to Uniswap directly and always will. This
/// contract is the OPT-IN premium lane:
///
///   - zapMint: pull one token, take the 0.1% platform fee, optionally swap a
///     slice, mint the position — NFT goes straight to the user. Funds only
///     exist here inside the transaction; nothing rests in this contract.
///   - TP/SL: for positions minted here (or retro-enabled), the owner stores
///     trigger ticks. Our keeper may then close the position when BOTH the
///     spot tick and a 60s TWAP breach the trigger — and the proceeds can go
///     nowhere except the position owner's wallet.
///
/// Trust surface, in full:
///   - PLATFORM_FEE_BPS and feeSink are immutable. The fee can never be raised.
///   - The owner can only rotate the keeper address and set the retro-enable
///     price (hard-capped). No pause, no upgrade, no fund access.
///   - A compromised keeper can at worst trigger a close early, bounded by
///     the owner's own slippage setting and the TWAP guard.
///   - The user can revoke the NFT approval at any time and manage the
///     position on Uniswap directly. Features are gated here, funds never.
contract AlpsRouter is Ownable2Step, ReentrancyGuard {
    using SafeERC20 for OZIERC20;

    // ---------------------------------------------------------- constants --
    uint256 public constant PLATFORM_FEE_BPS = 10; // 0.1%, immutable forever
    uint256 public constant BPS = 10_000;
    uint64 public constant ORDER_TTL = 30 days;
    uint256 public constant MAX_RETRO_FEE = 0.01 ether;
    // Robinhood Chain is a ~100ms-block Orbit chain; Uniswap's oracle still
    // writes at most one observation per SECOND. A fixed 60s lookback reverts
    // 'OLD' on young pools and is griefable, so the trigger TWAP is ADAPTIVE:
    // it uses the largest window the pool can answer, capped at MAX and floored
    // at MIN (below the floor executeOrder fails closed until history accrues).
    uint32 public constant TWAP_MAX = 30; // seconds — manipulation-resistance cap
    uint32 public constant TWAP_MIN = 5; // seconds — refuse to trigger on less
    uint16 public constant OBS_CARDINALITY = 64; // one-time per-pool ring size
    uint256 public constant MAX_SLIPPAGE_BPS = 500; // 5%

    INonfungiblePositionManager public immutable npm;
    ISwapRouter02 public immutable swapRouter;
    IUniswapV3Factory public immutable factory;
    IWETH public immutable weth;
    address public immutable feeSink;

    // ------------------------------------------------------------ storage --
    /// premium gate: positions minted through this contract (or retro-enabled)
    mapping(uint256 => address) public mintedBy;

    struct Order {
        // trigger when tick <= tickBelow (stop side) or tick >= tickAbove
        // (target side); use sentinels to disable a side
        int24 tickBelow;
        int24 tickAbove;
        uint16 slippageBps;
        uint64 expiry;
        // owner at registration — a transfer invalidates the order so the
        // previous owner's triggers can never close the new owner's position
        address owner;
    }

    mapping(uint256 => Order) public orders;

    address public keeper;
    uint256 public retroEnableFeeWei = 0.002 ether;

    // ------------------------------------------------------------- events --
    event ZapMinted(
        address indexed user,
        uint256 indexed tokenId,
        address tokenIn,
        uint256 amountIn,
        uint256 feeTaken,
        uint128 liquidity
    );
    event OrderRegistered(
        uint256 indexed tokenId, int24 tickBelow, int24 tickAbove, uint16 slippageBps, uint64 expiry
    );
    event OrderCancelled(uint256 indexed tokenId);
    event OrderExecuted(
        uint256 indexed tokenId, address indexed owner, int24 spotTick, uint256 amount0, uint256 amount1
    );
    event RetroEnabled(uint256 indexed tokenId, address indexed user, uint256 feePaid);
    event KeeperChanged(address indexed keeper);
    event RetroEnableFeeChanged(uint256 feeWei);

    // ------------------------------------------------------------- errors --
    error NotKeeper();
    error NotPositionOwner();
    error NotPremium();
    error NoOrder();
    error OrderExpired();
    error NotTriggered();
    error NotApproved();
    error BadParams();
    error FeeTooHigh();
    error NativeMismatch();
    error StaleOracle();

    constructor(
        address npm_,
        address swapRouter_,
        address factory_,
        address weth_,
        address feeSink_,
        address keeper_
    ) Ownable(msg.sender) {
        if (
            npm_ == address(0) || swapRouter_ == address(0) || factory_ == address(0)
                || weth_ == address(0) || feeSink_ == address(0)
        ) revert BadParams();
        npm = INonfungiblePositionManager(npm_);
        swapRouter = ISwapRouter02(swapRouter_);
        factory = IUniswapV3Factory(factory_);
        weth = IWETH(weth_);
        feeSink = feeSink_;
        keeper = keeper_;
    }

    // only WETH may send native here (unwrap during refunds)
    receive() external payable {
        if (msg.sender != address(weth)) revert BadParams();
    }

    // -------------------------------------------------------------- admin --
    function setKeeper(address keeper_) external onlyOwner {
        keeper = keeper_;
        emit KeeperChanged(keeper_);
    }

    function setRetroEnableFee(uint256 feeWei) external onlyOwner {
        if (feeWei > MAX_RETRO_FEE) revert FeeTooHigh();
        retroEnableFeeWei = feeWei;
        emit RetroEnableFeeChanged(feeWei);
    }

    // ---------------------------------------------------------------- zap --
    struct ZapParams {
        address tokenIn; // must be token0 or token1 of the target pool
        uint256 amountIn; // total deposit, fee comes out of this
        address token0;
        address token1;
        uint24 poolFee;
        int24 tickLower;
        int24 tickUpper;
        uint256 swapAmountIn; // slice of the net deposit swapped into the other token (0 = single-sided)
        uint24 swapPoolFee; // fee tier of the pool used for that swap
        uint256 swapMinOut;
        uint256 amount0Min; // mint slippage floors
        uint256 amount1Min;
        uint256 deadline;
    }

    /// Pull tokenIn (or native ETH when tokenIn is WETH), take the platform
    /// fee, optionally swap a slice, mint to msg.sender, refund every wei of
    /// leftovers. The NFT never belongs to this contract.
    function zapMint(ZapParams calldata p)
        external
        payable
        nonReentrant
        returns (uint256 tokenId, uint128 liquidity)
    {
        if (p.tokenIn != p.token0 && p.tokenIn != p.token1) revert BadParams();
        if (p.amountIn == 0) revert BadParams();

        // fund: native path wraps, ERC20 path pulls
        if (msg.value > 0) {
            if (p.tokenIn != address(weth) || msg.value != p.amountIn) revert NativeMismatch();
            weth.deposit{value: msg.value}();
        } else {
            OZIERC20(p.tokenIn).safeTransferFrom(msg.sender, address(this), p.amountIn);
        }

        // the 0.1% — the only value this contract ever keeps, and it goes
        // straight out to the immutable sink
        uint256 fee = (p.amountIn * PLATFORM_FEE_BPS) / BPS;
        if (fee > 0) OZIERC20(p.tokenIn).safeTransfer(feeSink, fee);
        uint256 net = p.amountIn - fee;

        address tokenOther = p.tokenIn == p.token0 ? p.token1 : p.token0;
        if (p.swapAmountIn > net) revert BadParams();
        if (p.swapAmountIn > 0) {
            OZIERC20(p.tokenIn).forceApprove(address(swapRouter), p.swapAmountIn);
            swapRouter.exactInputSingle(
                ISwapRouter02.ExactInputSingleParams({
                    tokenIn: p.tokenIn,
                    tokenOut: tokenOther,
                    fee: p.swapPoolFee,
                    recipient: address(this),
                    amountIn: p.swapAmountIn,
                    amountOutMinimum: p.swapMinOut,
                    sqrtPriceLimitX96: 0
                })
            );
        }

        uint256 bal0 = IERC20(p.token0).balanceOf(address(this));
        uint256 bal1 = IERC20(p.token1).balanceOf(address(this));
        OZIERC20(p.token0).forceApprove(address(npm), bal0);
        OZIERC20(p.token1).forceApprove(address(npm), bal1);

        (tokenId, liquidity,,) = npm.mint(
            INonfungiblePositionManager.MintParams({
                token0: p.token0,
                token1: p.token1,
                fee: p.poolFee,
                tickLower: p.tickLower,
                tickUpper: p.tickUpper,
                amount0Desired: bal0,
                amount1Desired: bal1,
                amount0Min: p.amount0Min,
                amount1Min: p.amount1Min,
                recipient: msg.sender,
                deadline: p.deadline
            })
        );

        mintedBy[tokenId] = msg.sender;

        // refund leftovers of both sides, unwrapping if the user paid native
        _refund(p.token0, msg.sender, msg.value > 0);
        _refund(p.token1, msg.sender, msg.value > 0);

        emit ZapMinted(msg.sender, tokenId, p.tokenIn, p.amountIn, fee, liquidity);
    }

    function _refund(address token, address to, bool paidNative) internal {
        uint256 bal = IERC20(token).balanceOf(address(this));
        if (bal == 0) return;
        if (paidNative && token == address(weth)) {
            weth.withdraw(bal);
            (bool ok,) = to.call{value: bal}("");
            if (!ok) revert BadParams();
        } else {
            OZIERC20(token).safeTransfer(to, bal);
        }
    }

    // -------------------------------------------------------- retro-enable --
    /// Bring an existing Uniswap-minted position into the premium lane.
    function enableExisting(uint256 tokenId) external payable nonReentrant {
        if (npm.ownerOf(tokenId) != msg.sender) revert NotPositionOwner();
        if (mintedBy[tokenId] != address(0)) revert BadParams();
        if (msg.value < retroEnableFeeWei) revert BadParams();
        (bool ok,) = feeSink.call{value: msg.value}("");
        if (!ok) revert BadParams();
        mintedBy[tokenId] = msg.sender;
        emit RetroEnabled(tokenId, msg.sender, msg.value);
    }

    // -------------------------------------------------------------- orders --
    function registerOrder(uint256 tokenId, int24 tickBelow, int24 tickAbove, uint16 slippageBps)
        external
    {
        // premium is a property of the TOKEN (minted here or retro-enabled),
        // so it survives transfers — any CURRENT owner of a premium position
        // may arm it. mintedBy is only a flag, never a control handle: every
        // path also requires current ownership.
        if (npm.ownerOf(tokenId) != msg.sender) revert NotPositionOwner();
        if (mintedBy[tokenId] == address(0)) revert NotPremium();
        if (slippageBps > MAX_SLIPPAGE_BPS) revert BadParams();
        // at least one live side, and a coherent window
        bool hasBelow = tickBelow > TickMath.MIN_TICK;
        bool hasAbove = tickAbove < TickMath.MAX_TICK;
        if (!hasBelow && !hasAbove) revert BadParams();
        if (hasBelow && hasAbove && tickBelow >= tickAbove) revert BadParams();

        orders[tokenId] = Order({
            tickBelow: tickBelow,
            tickAbove: tickAbove,
            slippageBps: slippageBps,
            expiry: uint64(block.timestamp) + ORDER_TTL,
            owner: msg.sender
        });

        // grow the pool's oracle ring so it can answer the trigger TWAP.
        // No-op (and free) on pools already at/above this size, e.g. the
        // busy majors. Fresh pools pay it once.
        (,, address t0, address t1, uint24 fee_,,,,,,,) = npm.positions(tokenId);
        IUniswapV3Pool pool = IUniswapV3Pool(factory.getPool(t0, t1, fee_));
        pool.increaseObservationCardinalityNext(OBS_CARDINALITY);

        emit OrderRegistered(tokenId, tickBelow, tickAbove, slippageBps, orders[tokenId].expiry);
    }

    function cancelOrder(uint256 tokenId) external {
        if (npm.ownerOf(tokenId) != msg.sender) revert NotPositionOwner();
        if (orders[tokenId].expiry == 0) revert NoOrder();
        delete orders[tokenId];
        emit OrderCancelled(tokenId);
    }

    /// Keeper-triggered close. Fails closed on every uncertainty: no order,
    /// expired, no approval, trigger not breached by BOTH spot and TWAP, or
    /// proceeds under the owner's slippage floor.
    function executeOrder(uint256 tokenId) external nonReentrant {
        if (msg.sender != keeper) revert NotKeeper();
        Order memory o = orders[tokenId];
        if (o.expiry == 0) revert NoOrder();
        if (block.timestamp > o.expiry) revert OrderExpired();

        address owner_ = npm.ownerOf(tokenId);
        // a transferred position drops its old order: the trigger belongs to
        // whoever set it, and must never fire for a new owner
        if (owner_ != o.owner) revert NotPositionOwner();
        if (!npm.isApprovedForAll(owner_, address(this)) && npm.getApproved(tokenId) != address(this))
        {
            revert NotApproved();
        }

        (,, address t0, address t1, uint24 fee_, int24 tickLower, int24 tickUpper, uint128 liq,,,,) =
            npm.positions(tokenId);
        if (liq == 0) revert NoOrder();
        IUniswapV3Pool pool = IUniswapV3Pool(factory.getPool(t0, t1, fee_));

        // trigger requires BOTH spot and an adaptive TWAP to breach — a single
        // manipulated block cannot force a close
        (uint160 spotSqrtP, int24 spotTick,,,,,) = pool.slot0();
        int24 twapTick = _consultTwap(pool);
        bool below = spotTick <= o.tickBelow && twapTick <= o.tickBelow;
        bool above = spotTick >= o.tickAbove && twapTick >= o.tickAbove;
        if (!below && !above) revert NotTriggered();

        // slippage floor from the SPOT price where the burn actually settles —
        // floors from the (divergent) TWAP made decreaseLiquidity revert during
        // fast moves, the exact moment a stop must fire.
        //
        // INVARIANT (load-bearing, do not break in any refactor): this floor is
        // safe ONLY because executeOrder performs NO swap and collect() below
        // hard-routes proceeds to the position owner. A pure liquidity removal
        // cannot be sandwiched for the owner's loss (LP value is convex, minimal
        // at the true price). If a swap or a non-owner recipient is ever added
        // here, this spot-derived floor stops protecting and must be replaced.
        (uint256 exp0, uint256 exp1) = LiquidityAmounts.getAmountsForLiquidity(
            spotSqrtP,
            TickMath.getSqrtRatioAtTick(tickLower),
            TickMath.getSqrtRatioAtTick(tickUpper),
            liq
        );

        delete orders[tokenId];

        npm.decreaseLiquidity(
            INonfungiblePositionManager.DecreaseLiquidityParams({
                tokenId: tokenId,
                liquidity: liq,
                amount0Min: (exp0 * (BPS - o.slippageBps)) / BPS,
                amount1Min: (exp1 * (BPS - o.slippageBps)) / BPS,
                deadline: block.timestamp
            })
        );
        // principal + all accrued fees, straight to the position owner —
        // there is no code path that pays anyone else
        (uint256 amount0, uint256 amount1) = npm.collect(
            INonfungiblePositionManager.CollectParams({
                tokenId: tokenId,
                recipient: owner_,
                amount0Max: type(uint128).max,
                amount1Max: type(uint128).max
            })
        );

        emit OrderExecuted(tokenId, owner_, spotTick, amount0, amount1);
    }

    /// Adaptive-window TWAP tick. Uses the largest window the pool's oracle
    /// can answer (so it never reverts 'OLD'), capped at TWAP_MAX and floored
    /// at TWAP_MIN — under the floor it reverts StaleOracle (fail closed) so a
    /// brand-new pool cannot trigger on a meaninglessly short average.
    function _consultTwap(IUniswapV3Pool pool) internal view returns (int24) {
        uint32 window = _availableWindow(pool);
        if (window < TWAP_MIN) revert StaleOracle();

        uint32[] memory ago = new uint32[](2);
        ago[0] = window;
        ago[1] = 0;
        (int56[] memory cum,) = pool.observe(ago);
        int56 delta = cum[1] - cum[0];
        int24 tick = int24(delta / int56(uint56(window)));
        // round toward negative infinity, matching Uniswap's OracleLibrary
        if (delta < 0 && (delta % int56(uint56(window)) != 0)) tick--;
        return tick;
    }

    /// Seconds of history actually available, capped at TWAP_MAX. Mirrors
    /// OracleLibrary.getOldestObservationSecondsAgo: the slot after the current
    /// index holds the oldest observation once the ring is full; before that
    /// the oldest is index 0.
    function _availableWindow(IUniswapV3Pool pool) internal view returns (uint32) {
        (,, uint16 index, uint16 cardinality,,,) = pool.slot0();
        if (cardinality == 0) return 0;
        uint256 oldestIndex = (uint256(index) + 1) % cardinality;
        (uint32 ts,,, bool initialized) = pool.observations(oldestIndex);
        if (!initialized) {
            (ts,,,) = pool.observations(0);
        }
        // wrap deliberately, as Uniswap's oracle does: the uint32 second clock
        // overflows by design, and elapsed = now - ts stays correct across it
        uint32 age;
        unchecked {
            age = uint32(block.timestamp) - ts;
        }
        return age > TWAP_MAX ? TWAP_MAX : age;
    }
}
Chain explorer3990msChain node87ms