LauncherLocker
0x173d8370b4f67535d406f2f46168ec48aa03d26e
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
2,673 bytes
ABI entries
25
6 read · 6 write
License
none
Contract information
- Address
- 0x173d8370b4f67535d406f2f46168ec48aa03d26e
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0xE9C1a82D6b…eACCe42A1c
- Creation tx
- 0x210545dc6e…90859f16f2
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (6)
factory() → address
feeRouter() → address
feeWalletOf(address) → address
getPosition(address) → tuple
onERC721Received(address, address, uint256, bytes) → bytes4
owner() → address
Events (5)
FactoryUpdatedFeeRedirectUpdatedFeesClaimedOwnershipTransferredPositionRegistered
ABI
[
{
"inputs": [
{
"internalType": "address payable",
"name": "feeRouter_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "AlreadyRegistered",
"type": "error"
},
{
"inputs": [],
"name": "NotFactory",
"type": "error"
},
{
"inputs": [],
"name": "NotFeeWallet",
"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": "UnknownToken",
"type": "error"
},
{
"inputs": [],
"name": "ZeroAddress",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "factory",
"type": "address"
}
],
"name": "FactoryUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newFeeWallet",
"type": "address"
}
],
"name": "FeeRedirectUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "positionId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "tokenAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "pairAmount",
"type": "uint256"
}
],
"name": "FeesClaimed",
"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": "positionId",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "feeWallet",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "positionManager",
"type": "address"
}
],
"name": "PositionRegistered",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "claimFees",
"outputs": [
{
"internalType": "uint256",
"name": "tokenAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "pairAmount",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "factory",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "feeRouter",
"outputs": [
{
"internalType": "contract FeeRouter",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "feeWalletOf",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "getPosition",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "positionId",
"type": "uint256"
},
{
"internalType": "address",
"name": "positionManager",
"type": "address"
},
{
"internalType": "address",
"name": "pairToken",
"type": "address"
},
{
"internalType": "address",
"name": "feeWallet",
"type": "address"
},
{
"internalType": "bool",
"name": "isToken0",
"type": "bool"
}
],
"internalType": "struct LauncherLocker.Position",
"name": "",
"type": "tuple"
}
],
"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": "pure",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "positionId",
"type": "uint256"
},
{
"internalType": "address",
"name": "positionManager",
"type": "address"
},
{
"internalType": "address",
"name": "pairToken",
"type": "address"
},
{
"internalType": "address",
"name": "feeWallet",
"type": "address"
},
{
"internalType": "bool",
"name": "isToken0",
"type": "bool"
}
],
"name": "registerPosition",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "factory_",
"type": "address"
}
],
"name": "setFactory",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "newFeeWallet",
"type": "address"
}
],
"name": "setFeeRedirect",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {INonfungiblePositionManager} from "./interfaces/IUniswapV3.sol";
import {FeeRouter} from "./FeeRouter.sol";
/// @notice Permanently holds LP NFTs for launched tokens. There is no withdraw
/// path: liquidity is locked forever. Anyone may trigger a fee claim for a token;
/// proceeds are split by the FeeRouter between the token's feeWallet and the
/// protocol treasury.
contract LauncherLocker is Ownable {
using SafeERC20 for IERC20;
error NotFactory();
error NotFeeWallet();
error UnknownToken();
error ZeroAddress();
error AlreadyRegistered();
event PositionRegistered(
address indexed token, uint256 indexed positionId, address indexed feeWallet, address positionManager
);
event FeesClaimed(address indexed token, uint256 indexed positionId, uint256 tokenAmount, uint256 pairAmount);
event FeeRedirectUpdated(address indexed token, address indexed newFeeWallet);
event FactoryUpdated(address factory);
struct Position {
uint256 positionId;
address positionManager;
address pairToken;
address feeWallet;
bool isToken0; // launched token is token0 of the pool
}
FeeRouter public immutable feeRouter;
address public factory;
mapping(address token => Position) internal positions;
constructor(address payable feeRouter_) Ownable(msg.sender) {
if (feeRouter_ == address(0)) revert ZeroAddress();
feeRouter = FeeRouter(feeRouter_);
}
/// @notice One-time wiring of the factory (set at deployment).
function setFactory(address factory_) external onlyOwner {
if (factory_ == address(0)) revert ZeroAddress();
factory = factory_;
emit FactoryUpdated(factory_);
}
/// @notice Called by the factory right after minting the LP NFT to this contract.
function registerPosition(
address token,
uint256 positionId,
address positionManager,
address pairToken,
address feeWallet,
bool isToken0
) external {
if (msg.sender != factory) revert NotFactory();
if (positions[token].positionManager != address(0)) revert AlreadyRegistered();
positions[token] = Position({
positionId: positionId,
positionManager: positionManager,
pairToken: pairToken,
feeWallet: feeWallet,
isToken0: isToken0
});
emit PositionRegistered(token, positionId, feeWallet, positionManager);
}
/// @notice Current fee recipient can redirect future fee payouts.
function setFeeRedirect(address token, address newFeeWallet) external {
Position storage pos = positions[token];
if (pos.positionManager == address(0)) revert UnknownToken();
if (msg.sender != pos.feeWallet) revert NotFeeWallet();
if (newFeeWallet == address(0)) revert ZeroAddress();
pos.feeWallet = newFeeWallet;
emit FeeRedirectUpdated(token, newFeeWallet);
}
/// @notice Collect accrued LP fees for `token` and distribute via the FeeRouter.
/// Callable by anyone; funds always flow to feeWallet + treasury.
function claimFees(address token) external returns (uint256 tokenAmount, uint256 pairAmount) {
Position memory pos = positions[token];
if (pos.positionManager == address(0)) revert UnknownToken();
(uint256 amount0, uint256 amount1) = INonfungiblePositionManager(pos.positionManager).collect(
INonfungiblePositionManager.CollectParams({
tokenId: pos.positionId,
recipient: address(feeRouter),
amount0Max: type(uint128).max,
amount1Max: type(uint128).max
})
);
(tokenAmount, pairAmount) = pos.isToken0 ? (amount0, amount1) : (amount1, amount0);
feeRouter.distribute(token, pos.pairToken, tokenAmount, pairAmount, pos.feeWallet);
emit FeesClaimed(token, pos.positionId, tokenAmount, pairAmount);
}
/// @notice Pending (uncollected) fees are read off-chain via positionManager.positions().
function getPosition(address token) external view returns (Position memory) {
Position memory pos = positions[token];
if (pos.positionManager == address(0)) revert UnknownToken();
return pos;
}
function feeWalletOf(address token) external view returns (address) {
Position memory pos = positions[token];
if (pos.positionManager == address(0)) revert UnknownToken();
return pos.feeWallet;
}
/// @notice Accept LP NFTs minted directly to this contract.
function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
return this.onERC721Received.selector;
}
}
Chain explorer2206msChain node78ms