// SPDX-License-Identifier: MIT
pragma solidity =0.8.23;
import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "./interfaces/INonfungiblePositionManager.sol";
/// @author web3roy
/// @title LiquidityManager
/// @dev Manages liquidity positions for tokens on a decentralized exchange.
/// Includes functionalities for fee collection, burning NFTs, and tracking token positions.
/// Ensures secure and efficient management of liquidity.
contract LiquidityManager is IERC721Receiver, Ownable2Step {
event Constructor(address positionManager);
event FeesCollected(
address indexed collector,
uint256 amount0,
uint256 amount1
);
event SetAuthorizedAddress(address indexed addr, bool isAuthorized);
event NFTBurned(address indexed burner, uint256 tokenId);
event TokenReceived(
address operator,
address from,
uint256 tokenId,
uint256 blockTime
);
uint256[] public tokenList;
mapping(uint256 => uint256) public tokenReceivedTime;
INonfungiblePositionManager public positionManager;
mapping(address => bool) public isAuthorized;
/// @notice Constructor to initialize the LiquidityManager with a position manager address
/// @param _positionManager The address of the Uniswap V3 Nonfungible Position Manager
constructor(address _positionManager) Ownable(msg.sender) {
require(_positionManager != address(0), "Invalid address");
positionManager = INonfungiblePositionManager(_positionManager);
emit Constructor(_positionManager);
}
receive() external payable {}
/// @notice Modifier to check if the caller is authorized to collect fees
modifier onlyAuthorized() {
require(isAuthorized[msg.sender], "Not authorized to collect fees");
_;
}
/// @notice Sets the authorization status of an address
/// @param _address The address to authorize or deauthorize
/// @param _isAuthorized The authorization status (true to authorize, false to deauthorize)
function setAuthorizedAddress(
address _address,
bool _isAuthorized
) external onlyOwner {
require(_address != address(0), "Invalid address");
isAuthorized[_address] = _isAuthorized;
emit SetAuthorizedAddress(_address, _isAuthorized);
}
/// @notice Collects fees for a specific liquidity position
/// @param tokenId The ID of the liquidity position NFT
function collectFees(uint256 tokenId) external onlyAuthorized {
(uint256 amount0, uint256 amount1) = positionManager.collect(
INonfungiblePositionManager.CollectParams({
tokenId: tokenId,
recipient: msg.sender,
amount0Max: type(uint128).max,
amount1Max: type(uint128).max
})
);
emit FeesCollected(msg.sender, amount0, amount1);
}
/// @notice Burns a specific liquidity position NFT
/// @param tokenId The ID of the liquidity position NFT to burn
function burnNFT(uint256 tokenId) external onlyOwner {
require(
block.timestamp >= tokenReceivedTime[tokenId] + 365 days,
"Cannot burn before one year"
);
positionManager.safeTransferFrom(
address(this),
0x000000000000000000000000000000000000dEaD,
tokenId
);
emit NFTBurned(msg.sender, tokenId);
}
/// @notice Retrieves the list of token IDs
/// @return An array of token IDs
function getTokenList() external view returns (uint256[] memory) {
return tokenList;
}
function onERC721Received(
address _operator,
address _from,
uint256 _tokenId,
bytes calldata
) external override returns (bytes4) {
require(
msg.sender == address(positionManager),
"NFT not from positionManager"
);
tokenList.push(_tokenId);
tokenReceivedTime[_tokenId] = block.timestamp;
emit TokenReceived(_operator, _from, _tokenId, block.timestamp);
return this.onERC721Received.selector;
}
/// @notice Prevent accidental renouncement of ownership
function renounceOwnership() public view override onlyOwner {
revert("Cannot renounce ownership");
}
}[
{
"type": "constructor",
"inputs": [
{
"name": "_positionManager",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "nonpayable"
},
{
"name": "OwnableInvalidOwner",
"type": "error",
"inputs": [
{
"name": "owner",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "OwnableUnauthorizedAccount",
"type": "error",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "Constructor",
"type": "event",
"inputs": [
{
"name": "positionManager",
"type": "address",
"indexed": false,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "FeesCollected",
"type": "event",
"inputs": [
{
"name": "collector",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "amount0",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "amount1",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "NFTBurned",
"type": "event",
"inputs": [
{
"name": "burner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "OwnershipTransferStarted",
"type": "event",
"inputs": [
{
"name": "previousOwner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "newOwner",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "OwnershipTransferred",
"type": "event",
"inputs": [
{
"name": "previousOwner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "newOwner",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "SetAuthorizedAddress",
"type": "event",
"inputs": [
{
"name": "addr",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "isAuthorized",
"type": "bool",
"indexed": false,
"internalType": "bool"
}
],
"anonymous": false
},
{
"name": "TokenReceived",
"type": "event",
"inputs": [
{
"name": "operator",
"type": "address",
"indexed": false,
"internalType": "address"
},
{
"name": "from",
"type": "address",
"indexed": false,
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "blockTime",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "acceptOwnership",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "burnNFT",
"type": "function",
"inputs": [
{
"name": "tokenId",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "collectFees",
"type": "function",
"inputs": [
{
"name": "tokenId",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "getTokenList",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256[]",
"internalType": "uint256[]"
}
],
"stateMutability": "view"
},
{
"name": "isAuthorized",
"type": "function",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "onERC721Received",
"type": "function",
"inputs": [
{
"name": "_operator",
"type": "address",
"internalType": "address"
},
{
"name": "_from",
"type": "address",
"internalType": "address"
},
{
"name": "_tokenId",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [
{
"name": "",
"type": "bytes4",
"internalType": "bytes4"
}
],
"stateMutability": "nonpayable"
},
{
"name": "owner",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "pendingOwner",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "positionManager",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "contract INonfungiblePositionManager"
}
],
"stateMutability": "view"
},
{
"name": "renounceOwnership",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "view"
},
{
"name": "setAuthorizedAddress",
"type": "function",
"inputs": [
{
"name": "_address",
"type": "address",
"internalType": "address"
},
{
"name": "_isAuthorized",
"type": "bool",
"internalType": "bool"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "tokenList",
"type": "function",
"inputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "tokenReceivedTime",
"type": "function",
"inputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "transferOwnership",
"type": "function",
"inputs": [
{
"name": "newOwner",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "receive",
"stateMutability": "payable"
}
]0x60803461013757601f610b2b38819003918201601f19168301916001600160401b0383118484101761013c5780849260209460405283398101031261013757516001600160a01b038082169182900361013757331561011e5760018060a01b03199081600154166001556000543383821617600055604051913391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a382156100ea577f385bc760a35ad4b8b2c11c6a4c6d27daa5e72ce47836b34da36a3f1258ec45ec60208480856004541617600455604051908152a16040516109d890816101538239f35b62461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606490fd5b604051631e4fbdf760e01b815260006004820152602490fd5b600080fd5b634e487b7160e01b600052604160045260246000fdfe6080604081815260049182361015610022575b505050361561002057600080fd5b005b600092833560e01c918262063ee1146108b1575081631351cf51146107f2578163150b7a0214610696578163273cbaa0146105e35781632890e0d71461049d578163715018a61461043f578163791b98bc1461041757816379ba5097146103945781638da5cb5b1461036c5781639ead722214610333578163b17acdcd146101a357508063e30c39781461017b578063f2fde38b1461010b5763fe9fbb80146100cb5780610012565b346101075760203660031901126101075760209160ff9082906001600160a01b036100f46108d6565b1681526005855220541690519015158152f35b5080fd5b8234610178576020366003190112610178576101256108d6565b61012d610976565b600180546001600160a01b0319166001600160a01b0392831690811790915582549091167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b80fd5b503461010757816003193601126101075760015490516001600160a01b039091168152602090f35b9190503461032f57602090816003193601126102c4573384526005825260ff8185205416156102ee57825481516001600160a01b03949185166080820167ffffffffffffffff8111838210176102d957918760849286959486528435835287830198338a52868401906fffffffffffffffffffffffffffffffff828180945260608701928284528a519d8e9a8b9963fc6f786560e01b8b5251908a01525116602488015251166044860152511660648401525af180156102cf5784928591610297575b82519384528301523392507f2e4fb6077d4acf86e12bb7411fb82b2b3eaa6a49787f4b1e17b423e7ea84116991a280f35b9250508083813d83116102c8575b6102af818361093e565b810103126102c4578251928201518392610266565b8380fd5b503d6102a5565b81513d86823e3d90fd5b604184634e487b7160e01b6000525260246000fd5b5162461bcd60e51b815291820152601e60248201527f4e6f7420617574686f72697a656420746f20636f6c6c65637420666565730000604482015260649150fd5b8280fd5b90503461032f57602036600319011261032f573591600254831015610178575061035e6020926108f1565b91905490519160031b1c8152f35b505034610107578160031936011261010757905490516001600160a01b039091168152602090f35b9190503461032f578260031936011261032f57600154916001600160a01b039133838516036104005750506001600160a01b031991821660015582543392811683178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b60249250519063118cdaa760e01b82523390820152fd5b90503461032f578260031936011261032f575490516001600160a01b03909116815260209150f35b90508234610178578060031936011261017857506020606492610460610976565b5162461bcd60e51b815291820152601960248201527f43616e6e6f742072656e6f756e6365206f776e657273686970000000000000006044820152fd5b9190503461032f57602036600319011261032f578135916104bc610976565b8284526003602052818420546301e1338081018091116105d057421061058e5780548491906001600160a01b0316803b1561032f5782906064855180958193632142170760e11b8352308784015261dead60248401528960448401525af1801561058457610554575b5050519081527fd6a6bcd88cdc4656d5f4e14e7dfbc9bc3f2f9811f8d6b508f3bee7d999d4d41c60203392a280f35b67ffffffffffffffff829593951161057157508352913880610525565b634e487b7160e01b835260419052602482fd5b83513d87823e3d90fd5b6020606492519162461bcd60e51b8352820152601b60248201527f43616e6e6f74206275726e206265666f7265206f6e65207965617200000000006044820152fd5b634e487b7160e01b855260118252602485fd5b8284346101785780600319360112610178579080519182906002549182855260208095018093600284527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace90845b818110610682575050508161064791038261093e565b83519485948186019282875251809352850193925b82811061066b57505050500390f35b83518552869550938101939281019260010161065c565b825484529288019260019283019201610631565b828434610178576080366003190112610178576106b16108d6565b906024356001600160a01b038181169182900361032f576044359260643567ffffffffffffffff80821161032f573660238301121561032f578189013590811161032f5736910160240111610178578187541633036107af576002546801000000000000000081101561079c579260209695927f6bbd8cc735f8e4ac3637cc7c2a59670011eff2666cb47919f5ca1c075153a6109592610759866001608098016002556108f1565b81549060031b9086821b91600019901b19161790558381526003895287429120558651931683528683015284820152426060820152a151630a85bd0160e11b8152f35b634e487b7160e01b825260418852602482fd5b855162461bcd60e51b8152602081890152601c60248201527f4e4654206e6f742066726f6d20706f736974696f6e4d616e61676572000000006044820152606490fd5b9190503461032f578060031936011261032f5761080d6108d6565b90602435918215158093036108ad57610824610976565b6001600160a01b031692831561087957507fd696a3c16b032a69334595f76c5907927f5e62b4dd0455b61a3ae200f9656daf916020918486526005835280862060ff1981541660ff841617905551908152a280f35b6020606492519162461bcd60e51b8352820152600f60248201526e496e76616c6964206164647265737360881b6044820152fd5b8480fd5b929150346102c45760203660031901126102c457926020933581526003845220548152f35b600435906001600160a01b03821682036108ec57565b600080fd5b6002548110156109285760026000527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0190600090565b634e487b7160e01b600052603260045260246000fd5b90601f8019910116810190811067ffffffffffffffff82111761096057604052565b634e487b7160e01b600052604160045260246000fd5b6000546001600160a01b0316330361098a57565b60405163118cdaa760e01b8152336004820152602490fdfea2646970667358221220260bf1a04c05c5b27724c315c2e9f5ea0aa17807b49e20f62a52f2bd563b3fed64736f6c6343000817003300000000000000000000000073991a25c818bf1f1128deaab1492d45638de0d3
0x6080604081815260049182361015610022575b505050361561002057600080fd5b005b600092833560e01c918262063ee1146108b1575081631351cf51146107f2578163150b7a0214610696578163273cbaa0146105e35781632890e0d71461049d578163715018a61461043f578163791b98bc1461041757816379ba5097146103945781638da5cb5b1461036c5781639ead722214610333578163b17acdcd146101a357508063e30c39781461017b578063f2fde38b1461010b5763fe9fbb80146100cb5780610012565b346101075760203660031901126101075760209160ff9082906001600160a01b036100f46108d6565b1681526005855220541690519015158152f35b5080fd5b8234610178576020366003190112610178576101256108d6565b61012d610976565b600180546001600160a01b0319166001600160a01b0392831690811790915582549091167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b80fd5b503461010757816003193601126101075760015490516001600160a01b039091168152602090f35b9190503461032f57602090816003193601126102c4573384526005825260ff8185205416156102ee57825481516001600160a01b03949185166080820167ffffffffffffffff8111838210176102d957918760849286959486528435835287830198338a52868401906fffffffffffffffffffffffffffffffff828180945260608701928284528a519d8e9a8b9963fc6f786560e01b8b5251908a01525116602488015251166044860152511660648401525af180156102cf5784928591610297575b82519384528301523392507f2e4fb6077d4acf86e12bb7411fb82b2b3eaa6a49787f4b1e17b423e7ea84116991a280f35b9250508083813d83116102c8575b6102af818361093e565b810103126102c4578251928201518392610266565b8380fd5b503d6102a5565b81513d86823e3d90fd5b604184634e487b7160e01b6000525260246000fd5b5162461bcd60e51b815291820152601e60248201527f4e6f7420617574686f72697a656420746f20636f6c6c65637420666565730000604482015260649150fd5b8280fd5b90503461032f57602036600319011261032f573591600254831015610178575061035e6020926108f1565b91905490519160031b1c8152f35b505034610107578160031936011261010757905490516001600160a01b039091168152602090f35b9190503461032f578260031936011261032f57600154916001600160a01b039133838516036104005750506001600160a01b031991821660015582543392811683178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b60249250519063118cdaa760e01b82523390820152fd5b90503461032f578260031936011261032f575490516001600160a01b03909116815260209150f35b90508234610178578060031936011261017857506020606492610460610976565b5162461bcd60e51b815291820152601960248201527f43616e6e6f742072656e6f756e6365206f776e657273686970000000000000006044820152fd5b9190503461032f57602036600319011261032f578135916104bc610976565b8284526003602052818420546301e1338081018091116105d057421061058e5780548491906001600160a01b0316803b1561032f5782906064855180958193632142170760e11b8352308784015261dead60248401528960448401525af1801561058457610554575b5050519081527fd6a6bcd88cdc4656d5f4e14e7dfbc9bc3f2f9811f8d6b508f3bee7d999d4d41c60203392a280f35b67ffffffffffffffff829593951161057157508352913880610525565b634e487b7160e01b835260419052602482fd5b83513d87823e3d90fd5b6020606492519162461bcd60e51b8352820152601b60248201527f43616e6e6f74206275726e206265666f7265206f6e65207965617200000000006044820152fd5b634e487b7160e01b855260118252602485fd5b8284346101785780600319360112610178579080519182906002549182855260208095018093600284527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace90845b818110610682575050508161064791038261093e565b83519485948186019282875251809352850193925b82811061066b57505050500390f35b83518552869550938101939281019260010161065c565b825484529288019260019283019201610631565b828434610178576080366003190112610178576106b16108d6565b906024356001600160a01b038181169182900361032f576044359260643567ffffffffffffffff80821161032f573660238301121561032f578189013590811161032f5736910160240111610178578187541633036107af576002546801000000000000000081101561079c579260209695927f6bbd8cc735f8e4ac3637cc7c2a59670011eff2666cb47919f5ca1c075153a6109592610759866001608098016002556108f1565b81549060031b9086821b91600019901b19161790558381526003895287429120558651931683528683015284820152426060820152a151630a85bd0160e11b8152f35b634e487b7160e01b825260418852602482fd5b855162461bcd60e51b8152602081890152601c60248201527f4e4654206e6f742066726f6d20706f736974696f6e4d616e61676572000000006044820152606490fd5b9190503461032f578060031936011261032f5761080d6108d6565b90602435918215158093036108ad57610824610976565b6001600160a01b031692831561087957507fd696a3c16b032a69334595f76c5907927f5e62b4dd0455b61a3ae200f9656daf916020918486526005835280862060ff1981541660ff841617905551908152a280f35b6020606492519162461bcd60e51b8352820152600f60248201526e496e76616c6964206164647265737360881b6044820152fd5b8480fd5b929150346102c45760203660031901126102c457926020933581526003845220548152f35b600435906001600160a01b03821682036108ec57565b600080fd5b6002548110156109285760026000527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0190600090565b634e487b7160e01b600052603260045260246000fd5b90601f8019910116810190811067ffffffffffffffff82111761096057604052565b634e487b7160e01b600052604160045260246000fd5b6000546001600160a01b0316330361098a57565b60405163118cdaa760e01b8152336004820152602490fdfea2646970667358221220260bf1a04c05c5b27724c315c2e9f5ea0aa17807b49e20f62a52f2bd563b3fed64736f6c63430008170033
| Token | Symbol | Balance | Price | Value |
|---|---|---|---|---|
| no token holdings | ||||
| Type | Age | Block | Details |
|---|---|---|---|
| no cross-chain L1↔L2 transactions for this address | |||
| Transaction Hash | Method | Block | Age | From | To | Amount | Token | ||
|---|---|---|---|---|---|---|---|---|---|
| no token transfers for this address yet | |||||||||
| Txn Hash | Age | Event | Topics / Data |
|---|---|---|---|
| 0xcd201b…5de2a8 | 37 days agoFri, 10 Jul 2026 23:51:17 UTC | 0xd696a3…6daf | [0] 0x000000000000…88290208 data: 0x000000000000000000…00000001 |
| 0x1e19cb…b3cd85 | 37 days agoFri, 10 Jul 2026 23:51:08 UTC | 0x385bc7…45ec | data: 0x000000000000000000…638de0d3 |
| 0x1e19cb…b3cd85 | 37 days agoFri, 10 Jul 2026 23:51:08 UTC | 0x8be007…57e0 | [0] 0x000000000000…00000000 [1] 0x000000000000…88290208 |
| Transaction Hash | Method | Block | Age | From | To | Type | Item | ||
|---|---|---|---|---|---|---|---|---|---|
| no NFT transfers for this address yet | |||||||||
| Block | Age | Parent Transaction Hash | Type | Method | From | To | Value | |
|---|---|---|---|---|---|---|---|---|
| no internal transactions found for this address yet (traced blocks + on-demand) | ||||||||
| Transaction Hash | Method ? | Block | Age | From | To | Amount | Txn Fee ? | ||
|---|---|---|---|---|---|---|---|---|---|
| 0xcd201b…5de2a8 | setAuthorizedAddress | 6,489,069 | 37 days agoFri, 10 Jul 2026 23:51:17 UTC | 0xf61b…0208 | IN | LiquidityManager | $0.000 ETH | 0.00000238 |