StoxesFunV3Locker
0x134d32ea27ba2abb28cf210b5dc814f33ea570b5
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
6,176 bytes
ABI entries
48
21 read · 6 write
License
none
Contract information
- Address
- 0x134d32ea27ba2abb28cf210b5dc814f33ea570b5
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0x9Cf1e0bff5…CcA354c20B
- Creation tx
- 0x9c89369d79…f8a6a16e04
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (21)
BUYBACK_FEE_BPS() → uint256
CREATOR_FEE_BPS() → uint256
DEAD() → address
POOL_FEE() → uint24
WETH() → address
allLockedTokens(uint256) → address
allLockedTokensLength() → uint256
buybackExecutor() → address
creatorWalletOf(address) → address
factory() → address
locks(address) → uint256, address, address, bool
onERC721Received(address, address, uint256, bytes) → bytes4
owner() → address
pendingBuybackWeth(address) → uint256
positionIdOf(address) → uint256
positionManager() → address
stats(address) → uint256, uint256, uint256, uint256, uint64
statsOf(address) → uint256, uint256, uint256, uint256, uint256, uint64
swapRouter() → address
treasury() → address
usedTokenId(uint256) → bool
Events (8)
BuybackAccruedBuybackBurnedBuybackDeferredBuybackExecutorChangedFactoryChangedFeesCollectedOwnershipTransferredPositionLocked
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "positionManager_",
"type": "address"
},
{
"internalType": "address",
"name": "swapRouter_",
"type": "address"
},
{
"internalType": "address",
"name": "weth_",
"type": "address"
},
{
"internalType": "address",
"name": "treasury_",
"type": "address"
},
{
"internalType": "address",
"name": "admin_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "AlreadyLocked",
"type": "error"
},
{
"inputs": [],
"name": "InvalidParams",
"type": "error"
},
{
"inputs": [],
"name": "NotBuybackExecutor",
"type": "error"
},
{
"inputs": [],
"name": "NotFactory",
"type": "error"
},
{
"inputs": [],
"name": "NotOwner",
"type": "error"
},
{
"inputs": [],
"name": "NotPositionManager",
"type": "error"
},
{
"inputs": [],
"name": "NothingToBuyback",
"type": "error"
},
{
"inputs": [],
"name": "PositionNotOwned",
"type": "error"
},
{
"inputs": [],
"name": "Reentrancy",
"type": "error"
},
{
"inputs": [],
"name": "TransferFailed",
"type": "error"
},
{
"inputs": [],
"name": "UnknownToken",
"type": "error"
},
{
"inputs": [],
"name": "ZeroAddress",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "wethAdded",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "wethPending",
"type": "uint256"
}
],
"name": "BuybackAccrued",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "wethIn",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "tokensBurned",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "tokensBurnedDirect",
"type": "uint256"
}
],
"name": "BuybackBurned",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "wethPending",
"type": "uint256"
}
],
"name": "BuybackDeferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "oldExecutor",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newExecutor",
"type": "address"
}
],
"name": "BuybackExecutorChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "oldFactory",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newFactory",
"type": "address"
}
],
"name": "FactoryChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "caller",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "wethToCreator",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "wethToTreasury",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "tokenToCreator",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "tokenToTreasury",
"type": "uint256"
}
],
"name": "FeesCollected",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "creatorWallet",
"type": "address"
}
],
"name": "PositionLocked",
"type": "event"
},
{
"inputs": [],
"name": "BUYBACK_FEE_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "CREATOR_FEE_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "DEAD",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "POOL_FEE",
"outputs": [
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "WETH",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "allLockedTokens",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "allLockedTokensLength",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "buybackExecutor",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "collect",
"outputs": [
{
"internalType": "uint256",
"name": "wethAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tokenAmount",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "creatorWalletOf",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "factory",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "locks",
"outputs": [
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "creatorWallet",
"type": "address"
},
{
"internalType": "bool",
"name": "tokenIsToken0",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "onERC721Received",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "pendingBuybackWeth",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "positionIdOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "positionManager",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"internalType": "address",
"name": "creatorWallet",
"type": "address"
}
],
"name": "register",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "minTokensOut",
"type": "uint256"
}
],
"name": "runBuyback",
"outputs": [
{
"internalType": "uint256",
"name": "tokensBought",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newExecutor",
"type": "address"
}
],
"name": "setBuybackExecutor",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newFactory",
"type": "address"
}
],
"name": "setFactory",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "stats",
"outputs": [
{
"internalType": "uint256",
"name": "creatorWethPaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "creatorTokenPaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "buybackWethSpent",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tokensBurned",
"type": "uint256"
},
{
"internalType": "uint64",
"name": "lastCollectAt",
"type": "uint64"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "statsOf",
"outputs": [
{
"internalType": "uint256",
"name": "creatorWethPaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "creatorTokenPaid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "buybackWethSpent",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tokensBurned",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "pendingBuyback",
"type": "uint256"
},
{
"internalType": "uint64",
"name": "lastCollectAt",
"type": "uint64"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "swapRouter",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "treasury",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "usedTokenId",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {OwnableLite, ReentrancyGuardLite, SafeERC20Lite} from "./base/Common.sol";
import {INonfungiblePositionManager, ISwapRouter02} from "./interfaces/IUniswapV3.sol";
interface IERC20ApproveLite {
function approve(address spender, uint256 amount) external returns (bool);
}
/// @title StoxesFunV3Locker — permanent lock for stoxes.fun V3 launch liquidity
/// @author stoxes.fun
/// @notice Owns every non-taxed launch's Uniswap V3 position NFT FOREVER.
/// There is deliberately NO function that can transfer an NFT out or
/// decrease liquidity — once a position lands here, the liquidity is
/// locked for the life of the chain (bow.fun model). The only value
/// path is `collect()`: anyone may trigger a fee collection.
///
/// v3 fee split (both collected currencies):
/// - 17.5% -> the launch creator's immutable fee wallet
/// - 17.5% -> BUYBACK & BURN of the launch token:
/// * the WETH share accrues in `pendingBuybackWeth[token]`; the
/// swap itself is executed SEPARATELY via `runBuyback`, which
/// is executor-gated and takes a caller-supplied `minTokensOut`
/// slippage floor (a permissionless swap with no floor would be
/// sandwichable — the decoupling is the audit fix)
/// * the token-side share is burned directly to 0xdead
/// - 65.0% -> the platform treasury (remainder)
///
/// If the buyback swap fails (e.g. pool state edge), the WETH stays
/// parked in `pendingBuybackWeth[token]` and is retried on the next
/// `runBuyback` — a swap failure can never brick fee collection, and
/// no value is ever stranded.
///
/// Per-token cumulative counters (`stats`) expose creator fees paid,
/// WETH spent on buybacks, tokens burned, and the last collect time —
/// everything a UI needs to show the split publicly, on-chain.
///
/// The owner (platform admin) can only rotate the factory pointer for
/// FUTURE registrations (mirrors the Earnings splitter's setFactory) —
/// it has no path to locked NFTs, recorded creator wallets, or fees.
contract StoxesFunV3Locker is OwnableLite, ReentrancyGuardLite {
using SafeERC20Lite for address;
/// @notice Creator share of collected pool fees, in bps.
uint256 public constant CREATOR_FEE_BPS = 1750;
/// @notice Buyback-and-burn share of collected pool fees, in bps.
uint256 public constant BUYBACK_FEE_BPS = 1750;
/// @notice The 1% fee tier every stoxes.fun V3 launch pool uses.
uint24 public constant POOL_FEE = 10_000;
/// @notice Canonical burn address.
address public constant DEAD = 0x000000000000000000000000000000000000dEaD;
/// @notice The Uniswap V3 NonfungiblePositionManager on Robinhood Chain.
address public immutable positionManager;
/// @notice The canonical SwapRouter02 used for buyback swaps.
address public immutable swapRouter;
/// @notice WETH on Robinhood Chain.
address public immutable WETH;
/// @notice Platform treasury receiving the 65% fee share. Immutable.
address public immutable treasury;
/// @notice The stoxes.fun V3 launch factory allowed to register locks.
address public factory;
/// @notice The wallet allowed to execute buyback swaps (the keeper). The
/// owner may rotate it; it has NO other powers — the swap output
/// always goes to 0xdead and the input is the parked buyback WETH.
address public buybackExecutor;
/// @notice Position NFT ids already registered (a tokenId can never back
/// two lock records).
mapping(uint256 => bool) public usedTokenId;
struct Lock {
uint256 tokenId;
address token;
/// @dev Immutable after registration — creator fee payouts always land here.
address creatorWallet;
/// @dev True when the launch token is token0 in the pool (token < WETH).
bool tokenIsToken0;
}
/// @notice Public, cumulative per-token fee-split accounting.
struct TokenStats {
uint256 creatorWethPaid; // lifetime WETH paid to the creator wallet
uint256 creatorTokenPaid; // lifetime launch tokens paid to the creator wallet
uint256 buybackWethSpent; // lifetime WETH swapped into buyback burns
uint256 tokensBurned; // lifetime launch tokens sent to 0xdead by collect
uint64 lastCollectAt; // timestamp of the last collect() for this token
}
/// @notice token address => its permanent lock record.
mapping(address => Lock) public locks;
/// @notice token address => cumulative fee-split stats.
mapping(address => TokenStats) public stats;
/// @notice Buyback WETH parked after a failed swap, retried next collect.
mapping(address => uint256) public pendingBuybackWeth;
/// @notice All locked launch tokens, in registration order.
address[] public allLockedTokens;
event FactoryChanged(address indexed oldFactory, address indexed newFactory);
event PositionLocked(
address indexed token, uint256 indexed tokenId, address indexed creatorWallet
);
event FeesCollected(
address indexed token,
address indexed caller,
uint256 wethToCreator,
uint256 wethToTreasury,
uint256 tokenToCreator,
uint256 tokenToTreasury
);
event BuybackBurned(
address indexed token, uint256 wethIn, uint256 tokensBurned, uint256 tokensBurnedDirect
);
event BuybackDeferred(address indexed token, uint256 wethPending);
event BuybackAccrued(address indexed token, uint256 wethAdded, uint256 wethPending);
event BuybackExecutorChanged(address indexed oldExecutor, address indexed newExecutor);
error NotFactory();
error NotPositionManager();
error NotBuybackExecutor();
error InvalidParams();
error AlreadyLocked();
error UnknownToken();
error PositionNotOwned();
error NothingToBuyback();
constructor(
address positionManager_,
address swapRouter_,
address weth_,
address treasury_,
address admin_
) {
if (
positionManager_ == address(0) || swapRouter_ == address(0) || weth_ == address(0)
|| treasury_ == address(0)
) {
revert InvalidParams();
}
positionManager = positionManager_;
swapRouter = swapRouter_;
WETH = weth_;
treasury = treasury_;
buybackExecutor = admin_;
_initOwner(admin_);
}
/// @notice Rotates the buyback executor (the keeper wallet driving
/// `runBuyback`). Executor powers stop at triggering the swap —
/// output is always burned, input is always the parked WETH.
function setBuybackExecutor(address newExecutor) external onlyOwner {
if (newExecutor == address(0)) revert InvalidParams();
emit BuybackExecutorChanged(buybackExecutor, newExecutor);
buybackExecutor = newExecutor;
}
/// @notice Points the locker at the launch factory (or a rotated one).
/// Affects FUTURE registrations only; existing locks are untouched.
function setFactory(address newFactory) external onlyOwner {
if (newFactory == address(0)) revert InvalidParams();
emit FactoryChanged(factory, newFactory);
factory = newFactory;
}
/// @notice Records a freshly-minted launch position. Called by the factory
/// in the same transaction that mints the NFT to this locker.
/// @dev Hardened: the NFT must ALREADY be owned by this locker (a record
/// can never point at liquidity that is not actually locked here),
/// and a tokenId can never back two records.
function register(address token, uint256 tokenId, address creatorWallet) external {
if (msg.sender != factory) revert NotFactory();
if (token == address(0) || creatorWallet == address(0)) revert InvalidParams();
if (locks[token].creatorWallet != address(0)) revert AlreadyLocked();
if (usedTokenId[tokenId]) revert AlreadyLocked();
if (INonfungiblePositionManager(positionManager).ownerOf(tokenId) != address(this)) {
revert PositionNotOwned();
}
usedTokenId[tokenId] = true;
locks[token] = Lock({
tokenId: tokenId,
token: token,
creatorWallet: creatorWallet,
tokenIsToken0: token < WETH
});
allLockedTokens.push(token);
emit PositionLocked(token, tokenId, creatorWallet);
}
/// @notice PERMISSIONLESS fee collection: pulls all accrued pool fees for
/// `token`'s locked position and splits both currencies
/// 17.5% creator / 17.5% buyback&burn / 65% treasury. Anyone can
/// call ("COLLECT + BURN"); the caller never receives funds.
function collect(address token)
external
nonReentrant
returns (uint256 wethAmount, uint256 tokenAmount)
{
Lock memory lock = locks[token];
if (lock.creatorWallet == address(0)) revert UnknownToken();
(uint256 amount0, uint256 amount1) = INonfungiblePositionManager(positionManager).collect(
INonfungiblePositionManager.CollectParams({
tokenId: lock.tokenId,
recipient: address(this),
amount0Max: type(uint128).max,
amount1Max: type(uint128).max
})
);
(tokenAmount, wethAmount) =
lock.tokenIsToken0 ? (amount0, amount1) : (amount1, amount0);
uint256 wethToCreator = (wethAmount * CREATOR_FEE_BPS) / 10_000;
uint256 wethToBuyback = (wethAmount * BUYBACK_FEE_BPS) / 10_000;
uint256 wethToTreasury = wethAmount - wethToCreator - wethToBuyback;
uint256 tokenToCreator = (tokenAmount * CREATOR_FEE_BPS) / 10_000;
uint256 tokenToBurn = (tokenAmount * BUYBACK_FEE_BPS) / 10_000;
uint256 tokenToTreasury = tokenAmount - tokenToCreator - tokenToBurn;
if (wethToCreator > 0) WETH.safeTransfer(lock.creatorWallet, wethToCreator);
if (wethToTreasury > 0) WETH.safeTransfer(treasury, wethToTreasury);
if (tokenToCreator > 0) token.safeTransfer(lock.creatorWallet, tokenToCreator);
if (tokenToTreasury > 0) token.safeTransfer(treasury, tokenToTreasury);
// Direct burn of the token-side buyback share.
if (tokenToBurn > 0) token.safeTransfer(DEAD, tokenToBurn);
// Park the WETH buyback share; the swap runs separately via
// `runBuyback` with an executor-supplied slippage floor (collect is
// permissionless, so an in-line floorless swap would be sandwichable).
if (wethToBuyback > 0) {
uint256 newPending = pendingBuybackWeth[token] + wethToBuyback;
pendingBuybackWeth[token] = newPending;
emit BuybackAccrued(token, wethToBuyback, newPending);
}
TokenStats storage s = stats[token];
s.creatorWethPaid += wethToCreator;
s.creatorTokenPaid += tokenToCreator;
s.tokensBurned += tokenToBurn;
s.lastCollectAt = uint64(block.timestamp);
if (tokenToBurn > 0) emit BuybackBurned(token, 0, 0, tokenToBurn);
emit FeesCollected(
token, msg.sender, wethToCreator, wethToTreasury, tokenToCreator, tokenToTreasury
);
}
/// @notice Executes the parked buyback for `token`: swaps the accrued WETH
/// through the token's own pool straight to 0xdead, enforcing the
/// executor-supplied `minTokensOut` slippage floor (quoted off-chain
/// just before the call). Executor-gated because the floor is
/// caller-supplied; a failing swap re-parks the WETH.
function runBuyback(address token, uint256 minTokensOut)
external
nonReentrant
returns (uint256 tokensBought)
{
if (msg.sender != buybackExecutor) revert NotBuybackExecutor();
if (minTokensOut == 0) revert InvalidParams();
if (locks[token].creatorWallet == address(0)) revert UnknownToken();
uint256 buybackIn = pendingBuybackWeth[token];
if (buybackIn == 0) revert NothingToBuyback();
pendingBuybackWeth[token] = 0;
IERC20ApproveLite(WETH).approve(swapRouter, buybackIn);
try ISwapRouter02(swapRouter).exactInputSingle(
ISwapRouter02.ExactInputSingleParams({
tokenIn: WETH,
tokenOut: token,
fee: POOL_FEE,
recipient: DEAD,
amountIn: buybackIn,
amountOutMinimum: minTokensOut,
sqrtPriceLimitX96: 0
})
) returns (uint256 amountOut) {
tokensBought = amountOut;
TokenStats storage s = stats[token];
s.buybackWethSpent += buybackIn;
s.tokensBurned += tokensBought;
emit BuybackBurned(token, buybackIn, tokensBought, 0);
} catch {
IERC20ApproveLite(WETH).approve(swapRouter, 0);
pendingBuybackWeth[token] = buybackIn;
emit BuybackDeferred(token, buybackIn);
}
}
/// @notice The immutable creator fee wallet recorded for `token`
/// (zero address when the token is not locked here).
function creatorWalletOf(address token) external view returns (address) {
return locks[token].creatorWallet;
}
/// @notice The locked position NFT id for `token` (0 when unknown; check
/// `creatorWalletOf` for existence).
function positionIdOf(address token) external view returns (uint256) {
return locks[token].tokenId;
}
/// @notice Cumulative public fee-split stats for `token` in one call.
function statsOf(address token)
external
view
returns (
uint256 creatorWethPaid,
uint256 creatorTokenPaid,
uint256 buybackWethSpent,
uint256 tokensBurned,
uint256 pendingBuyback,
uint64 lastCollectAt
)
{
TokenStats memory s = stats[token];
return (
s.creatorWethPaid,
s.creatorTokenPaid,
s.buybackWethSpent,
s.tokensBurned,
pendingBuybackWeth[token],
s.lastCollectAt
);
}
function allLockedTokensLength() external view returns (uint256) {
return allLockedTokens.length;
}
/// @notice ERC-721 receive hook: accept position NFTs ONLY from the
/// position manager. Once here, an NFT can never leave — there is
/// no transfer/withdraw/decreaseLiquidity path in this contract.
function onERC721Received(address, address, uint256, bytes calldata)
external
view
returns (bytes4)
{
if (msg.sender != positionManager) revert NotPositionManager();
return this.onERC721Received.selector;
}
}
Chain explorer1238msChain node88ms