JellyfishLiquidityVault
0xa4fb977d00ede9f25741239796c22b81f2380888
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
2,363 bytes
ABI entries
29
5 read · 8 write
License
none
Contract information
- Address
- 0xa4fb977d00ede9f25741239796c22b81f2380888
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0x2c1b9cECeE…E4Dcb3Bf88
- Creation tx
- 0x84607ecc21…27e1abc3cf
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (5)
owner() → address
pendingOwner() → address
positionManager() → address
positionRecorded() → bool
positionTokenId() → uint256
Events (6)
NativeRescuedOwnershipTransferStartedOwnershipTransferredPositionRecordedPositionTransferredUnsupportedTokenRescued
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "positionManager_",
"type": "address"
},
{
"internalType": "address",
"name": "safeOwner",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "InvalidRecipient",
"type": "error"
},
{
"inputs": [],
"name": "NativeTransferFailed",
"type": "error"
},
{
"inputs": [],
"name": "OnlyPositionManager",
"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": "PositionAlreadyRecorded",
"type": "error"
},
{
"inputs": [],
"name": "PositionNotRecorded",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "SafeERC20FailedOperation",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "NativeRescued",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferStarted",
"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": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "PositionRecorded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"type": "address"
}
],
"name": "PositionTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "UnsupportedTokenRescued",
"type": "event"
},
{
"inputs": [],
"name": "acceptOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "onERC721Received",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingOwner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "positionManager",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "positionRecorded",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "positionTokenId",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "recordPosition",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address payable",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "rescueNative",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract IERC20",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "rescueUnsupportedToken",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
}
],
"name": "transferPosition",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/// @notice Safe-owned custody for the official PositionManager LP NFT.
/// @dev Nothing burns or removes liquidity automatically. Moving the LP NFT
/// requires an explicit transaction from the approved Safe owner.
contract JellyfishLiquidityVault is IERC721Receiver, Ownable2Step {
using SafeERC20 for IERC20;
address public immutable positionManager;
uint256 public positionTokenId;
bool public positionRecorded;
event PositionRecorded(uint256 indexed tokenId);
event PositionTransferred(uint256 indexed tokenId, address indexed recipient);
event UnsupportedTokenRescued(address indexed token, address indexed recipient, uint256 amount);
event NativeRescued(address indexed recipient, uint256 amount);
error OnlyPositionManager();
error PositionAlreadyRecorded();
error PositionNotRecorded();
error InvalidRecipient();
error NativeTransferFailed();
constructor(address positionManager_, address safeOwner) Ownable(safeOwner) {
if (positionManager_.code.length == 0 || safeOwner == address(0)) {
revert InvalidRecipient();
}
positionManager = positionManager_;
}
receive() external payable { }
function onERC721Received(address, address, uint256 tokenId, bytes calldata)
external
returns (bytes4)
{
if (msg.sender != positionManager) revert OnlyPositionManager();
_recordPosition(tokenId);
return IERC721Receiver.onERC721Received.selector;
}
function recordPosition(uint256 tokenId) external {
if (IERC721(positionManager).ownerOf(tokenId) != address(this)) {
revert OnlyPositionManager();
}
_recordPosition(tokenId);
}
function transferPosition(address recipient) external onlyOwner {
if (!positionRecorded) revert PositionNotRecorded();
if (recipient == address(0)) revert InvalidRecipient();
uint256 tokenId = positionTokenId;
positionRecorded = false;
IERC721(positionManager).safeTransferFrom(address(this), recipient, tokenId);
emit PositionTransferred(tokenId, recipient);
}
function rescueUnsupportedToken(IERC20 token, address recipient, uint256 amount)
external
onlyOwner
{
if (recipient == address(0)) revert InvalidRecipient();
token.safeTransfer(recipient, amount);
emit UnsupportedTokenRescued(address(token), recipient, amount);
}
function rescueNative(address payable recipient, uint256 amount) external onlyOwner {
if (recipient == address(0)) revert InvalidRecipient();
(bool success,) = recipient.call{ value: amount }("");
if (!success) revert NativeTransferFailed();
emit NativeRescued(recipient, amount);
}
function _recordPosition(uint256 tokenId) private {
if (positionRecorded) revert PositionAlreadyRecorded();
positionTokenId = tokenId;
positionRecorded = true;
emit PositionRecorded(tokenId);
}
}
Chain explorer3577msChain node83ms