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
- Creator
- 0xd51778f623…DED13626F9
- 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