SqueezBuyback

0x9b5cbc618d2570a2a6b50ca5acb538f6949ab963

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
2,996 bytes
ABI entries
13
4 read · 2 write
License
none

Contract information

Address
0x9b5cbc618d2570a2a6b50ca5acb538f6949ab963
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0xe38c43b355…b26b2080b4

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (4)

poolKey()address, address, uint24, int24, address
poolManager()address
squeez()address
weth()address

Events (1)

Buyback

ABI

[
  {
    "inputs": [
      {
        "internalType": "contract IPoolManager",
        "name": "poolManager_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "weth_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "squeez_",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "BadPool",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OnlyPoolManager",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ReentrancyGuardReentrantCall",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "SafeERC20FailedOperation",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "Slippage",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "caller",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "wethIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "squeezBurned",
        "type": "uint256"
      }
    ],
    "name": "Buyback",
    "type": "event"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "wethAmount",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "minOut",
        "type": "uint256"
      }
    ],
    "name": "buybackAndBurn",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "burned",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "poolKey",
    "outputs": [
      {
        "internalType": "Currency",
        "name": "currency0",
        "type": "address"
      },
      {
        "internalType": "Currency",
        "name": "currency1",
        "type": "address"
      },
      {
        "internalType": "uint24",
        "name": "fee",
        "type": "uint24"
      },
      {
        "internalType": "int24",
        "name": "tickSpacing",
        "type": "int24"
      },
      {
        "internalType": "contract IHooks",
        "name": "hooks",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "poolManager",
    "outputs": [
      {
        "internalType": "contract IPoolManager",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "squeez",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      }
    ],
    "name": "unlockCallback",
    "outputs": [
      {
        "internalType": "bytes",
        "name": "",
        "type": "bytes"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "weth",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  }
]

Source code

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

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 {IPoolManager} from "v4-core/interfaces/IPoolManager.sol";
import {IUnlockCallback} from "v4-core/interfaces/callback/IUnlockCallback.sol";
import {PoolKey} from "v4-core/types/PoolKey.sol";
import {Currency} from "v4-core/types/Currency.sol";
import {BalanceDelta} from "v4-core/types/BalanceDelta.sol";
import {SwapParams} from "v4-core/types/PoolOperation.sol";
import {TickMath} from "v4-core/libraries/TickMath.sol";

/// @notice Permissionless helper that buys the native SQUEEZ token with WETH through its Uniswap V4 pool
/// and burns everything it buys to 0x…dEaD. It swaps DIRECTLY against the PoolManager (via `unlock`) —
/// the canonical Universal Router cannot route these hooked V4 pools on Robinhood Chain, so the Squeez
/// keeper routes its protocol-fee buybacks through this contract instead. Holds no funds between calls;
/// any WETH the swap can't spend (price-limit) is returned to the caller. Emits `Buyback` for indexing.
contract SqueezBuyback is IUnlockCallback, ReentrancyGuard {
    using SafeERC20 for IERC20;

    address internal constant DEAD = 0x000000000000000000000000000000000000dEaD;

    IPoolManager public immutable poolManager;
    /// WETH — the pool's currency1 (swap input).
    address public immutable weth;
    /// SQUEEZ — the pool's currency0 (swap output, burned).
    address public immutable squeez;
    /// The SQUEEZ/WETH V4 pool key (currency0 == squeez, currency1 == weth).
    PoolKey public poolKey;

    error OnlyPoolManager();
    error Slippage();
    error BadPool();

    /// @param wethIn WETH actually spent buying SQUEEZ; `squeezBurned` the SQUEEZ bought and burned.
    event Buyback(address indexed caller, uint256 wethIn, uint256 squeezBurned);

    constructor(IPoolManager poolManager_, address weth_, address squeez_, PoolKey memory key) {
        // The pool must be SQUEEZ/WETH with SQUEEZ as currency0 (a launch token always sorts below its
        // quote), so a WETH→SQUEEZ buy is a currency1→currency0 (oneForZero) swap.
        if (Currency.unwrap(key.currency0) != squeez_ || Currency.unwrap(key.currency1) != weth_) revert BadPool();
        poolManager = poolManager_;
        weth = weth_;
        squeez = squeez_;
        poolKey = key;
    }

    /// @notice Pull `wethAmount` WETH from the caller (needs prior approval), swap it for SQUEEZ, and burn
    /// all of it to 0x…dEaD. `minOut` bounds slippage (revert if the swap yields less). Any WETH the swap
    /// couldn't spend (hit the price limit) is returned to the caller. Returns the SQUEEZ bought+burned.
    function buybackAndBurn(uint256 wethAmount, uint256 minOut) external nonReentrant returns (uint256 burned) {
        IERC20(weth).safeTransferFrom(msg.sender, address(this), wethAmount);
        uint256 spent = abi.decode(poolManager.unlock(abi.encode(wethAmount, minOut)), (uint256));
        // Burn the ACTUAL SQUEEZ balance received (SQUEEZ is elastic — avoid share-rounding dust reverts).
        burned = IERC20(squeez).balanceOf(address(this));
        if (burned != 0) IERC20(squeez).safeTransfer(DEAD, burned);
        uint256 leftover = wethAmount - spent;
        if (leftover != 0) IERC20(weth).safeTransfer(msg.sender, leftover);
        emit Buyback(msg.sender, spent, burned);
    }

    /// @dev V4 unlock callback: swap WETH→SQUEEZ, settle the WETH we owe, take the SQUEEZ out. Only the
    /// PoolManager may call it. Returns the WETH actually spent.
    function unlockCallback(bytes calldata data) external returns (bytes memory) {
        if (msg.sender != address(poolManager)) revert OnlyPoolManager();
        (uint256 wethIn, uint256 minOut) = abi.decode(data, (uint256, uint256));
        // Buy currency0 (SQUEEZ) with currency1 (WETH): zeroForOne = false, exact-input WETH.
        BalanceDelta d = poolManager.swap(
            poolKey,
            SwapParams({
                zeroForOne: false,
                amountSpecified: -int256(wethIn),
                sqrtPriceLimitX96: TickMath.MAX_SQRT_PRICE - 1
            }),
            ""
        );
        uint256 tokenOut = uint256(uint128(d.amount0())); // SQUEEZ received (owed to us)
        uint256 spent = uint256(uint128(-d.amount1())); // WETH spent (we owe)
        if (tokenOut < minOut) revert Slippage();
        // Pay the WETH we owe: sync the currency, transfer the real WETH to the PoolManager, settle.
        poolManager.sync(poolKey.currency1);
        if (spent != 0) IERC20(weth).safeTransfer(address(poolManager), spent);
        poolManager.settle();
        // Take the SQUEEZ output to this contract (the outer call burns it).
        if (tokenOut != 0) poolManager.take(poolKey.currency0, address(this), tokenOut);
        return abi.encode(spent);
    }
}
Chain explorer4286msChain node80ms