编译器
0.8.26+commit.8a97fa7a
文件 1 的 10:Address.sol
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
library Address {
error AddressEmptyCode(address target);
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert Errors.FailedCall();
}
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
function _revert(bytes memory returndata) private pure {
if (returndata.length > 0) {
assembly ("memory-safe") {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}
文件 2 的 10:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
文件 3 的 10:Errors.sol
pragma solidity ^0.8.20;
library Errors {
error InsufficientBalance(uint256 balance, uint256 needed);
error FailedCall();
error FailedDeployment();
error MissingPrecompile(address);
}
文件 4 的 10:IERC165.sol
pragma solidity ^0.8.20;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 5 的 10:IERC20.sol
pragma solidity ^0.8.20;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
文件 6 的 10:IERC721.sol
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
文件 7 的 10:IERC721Receiver.sol
pragma solidity ^0.8.20;
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
文件 8 的 10:IManager.sol
pragma solidity ^0.8.25;
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
interface NonFungibleContract is IERC721 {
function positions(
uint256 tokenId
)
external
view
returns (
uint96 nonce,
address operator,
address token0,
address token1,
uint24 fee,
int24 tickLower,
int24 tickUpper,
uint128 liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
);
struct CollectParams {
uint256 tokenId;
address recipient;
uint128 amount0Max;
uint128 amount1Max;
}
function collect(
CollectParams calldata params
) external payable returns (uint256 amount0, uint256 amount1);
}
文件 9 的 10:LpMetaLocker.sol
pragma solidity ^0.8.26;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {NonFungibleContract} from "./IManager.sol";
contract LpMetaLocker is Ownable, IERC721Receiver {
event Received(address indexed from, uint256 tokenId);
error NotOwner(address owner);
error InvalidTokenId(uint256 tokenId);
event ClaimedFees(
address indexed claimer,
address indexed token0,
address indexed token1,
uint256 amount0,
uint256 amount1,
uint256 totalAmount1,
uint256 totalAmount0
);
IERC721 private SafeERC721;
address private immutable e721Token;
address public positionManager = 0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1;
string public constant version = "0.0.1";
uint256 public _clankerTeamFee;
address public _clankerTeamRecipient;
struct UserFeeRecipient {
address recipient;
uint256 lpTokenId;
}
struct TeamFeeRecipient {
address recipient;
uint256 fee;
uint256 lpTokenId;
}
UserFeeRecipient[] public _userFeeRecipients;
mapping(uint256 => UserFeeRecipient) public _userFeeRecipientForToken;
mapping(uint256 => TeamFeeRecipient)
public _teamOverrideFeeRecipientForToken;
mapping(address => uint256[]) public _userTokenIds;
constructor(
address token,
address clankerTeamRecipient,
uint256 clankerTeamFee
) payable Ownable(clankerTeamRecipient) {
SafeERC721 = IERC721(token);
e721Token = token;
_clankerTeamFee = clankerTeamFee;
_clankerTeamRecipient = clankerTeamRecipient;
}
function setOverrideTeamFeesForToken(
uint256 tokenId,
address newTeamRecipient,
uint256 newTeamFee
) public onlyOwner {
_teamOverrideFeeRecipientForToken[tokenId] = TeamFeeRecipient({
recipient: newTeamRecipient,
fee: newTeamFee,
lpTokenId: tokenId
});
}
function updateClankerTeamFee(uint256 newFee) public onlyOwner {
_clankerTeamFee = newFee;
}
function updateClankerTeamRecipient(address newRecipient) public onlyOwner {
_clankerTeamRecipient = newRecipient;
}
receive() external payable virtual {}
function withdrawETH(address recipient) public onlyOwner {
payable(recipient).transfer(address(this).balance);
}
function withdrawERC20(address _token, address recipient) public onlyOwner {
IERC20 IToken = IERC20(_token);
IToken.transfer(recipient, IToken.balanceOf(address(this)));
}
function collectFees(uint256 _tokenId) public {
UserFeeRecipient memory userFeeRecipient = _userFeeRecipientForToken[
_tokenId
];
address _recipient = userFeeRecipient.recipient;
if (_recipient == address(0)) {
revert InvalidTokenId(_tokenId);
}
NonFungibleContract nonfungiblePositionManager = NonFungibleContract(
positionManager
);
(uint256 amount0, uint256 amount1) = nonfungiblePositionManager.collect(
NonFungibleContract.CollectParams({
recipient: address(this),
amount0Max: type(uint128).max,
amount1Max: type(uint128).max,
tokenId: _tokenId
})
);
(
,
,
address token0,
address token1,
,
,
,
,
,
,
,
) = nonfungiblePositionManager.positions(_tokenId);
IERC20 feeToken0 = IERC20(token0);
IERC20 feeToken1 = IERC20(token1);
address teamRecipient = _clankerTeamRecipient;
uint256 teamFee = _clankerTeamFee;
TeamFeeRecipient
memory overrideFeeRecipient = _teamOverrideFeeRecipientForToken[
_tokenId
];
if (overrideFeeRecipient.recipient != address(0)) {
teamRecipient = overrideFeeRecipient.recipient;
teamFee = overrideFeeRecipient.fee;
}
uint256 protocolFee0 = (amount0 * teamFee) / 100;
uint256 protocolFee1 = (amount1 * teamFee) / 100;
uint256 recipientFee0 = amount0 - protocolFee0;
uint256 recipientFee1 = amount1 - protocolFee1;
feeToken0.transfer(_recipient, recipientFee0);
feeToken1.transfer(_recipient, recipientFee1);
feeToken0.transfer(teamRecipient, protocolFee0);
feeToken1.transfer(teamRecipient, protocolFee1);
emit ClaimedFees(
_recipient,
token0,
token1,
recipientFee0,
recipientFee1,
amount0,
amount1
);
}
function getLpTokenIdsForUser(
address user
) public view returns (uint256[] memory) {
return _userTokenIds[user];
}
function setUserFeeRecipients(
UserFeeRecipient[] memory recipients
) public onlyOwner {
_userFeeRecipients = recipients;
for (uint256 i = 0; i < recipients.length; i++) {
_userFeeRecipientForToken[recipients[i].lpTokenId] = recipients[i];
_userTokenIds[recipients[i].recipient].push(
recipients[i].lpTokenId
);
}
}
function addUserFeeRecipient(
UserFeeRecipient memory recipient
) public onlyOwner {
_userFeeRecipients.push(recipient);
_userFeeRecipientForToken[recipient.lpTokenId] = recipient;
_userTokenIds[recipient.recipient].push(recipient.lpTokenId);
}
function onERC721Received(
address,
address from,
uint256 id,
bytes calldata
) external override returns (bytes4) {
if (from != _clankerTeamRecipient) {
revert NotOwner(from);
}
emit Received(from, id);
return IERC721Receiver.onERC721Received.selector;
}
}
文件 10 的 10:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
{
"compilationTarget": {
"src/LpMetaLocker.sol": "LpMetaLocker"
},
"evmVersion": "cancun",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
":@solady-v0.0.245/=lib/optimism/packages/contracts-bedrock/lib/solady/src/",
":@solady/=lib/optimism/packages/contracts-bedrock/lib/solady/src/",
":@uniswap/v3-core/=lib/v3-core/",
":ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":solady/=lib/solady/src/",
":v3-core/=lib/v3-core/"
],
"viaIR": true
}
[{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"clankerTeamRecipient","type":"address"},{"internalType":"uint256","name":"clankerTeamFee","type":"uint256"}],"stateMutability":"payable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"InvalidTokenId","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"NotOwner","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"claimer","type":"address"},{"indexed":true,"internalType":"address","name":"token0","type":"address"},{"indexed":true,"internalType":"address","name":"token1","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalAmount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalAmount0","type":"uint256"}],"name":"ClaimedFees","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":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Received","type":"event"},{"inputs":[],"name":"_clankerTeamFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_clankerTeamRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"_teamOverrideFeeRecipientForToken","outputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"lpTokenId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"_userFeeRecipientForToken","outputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"lpTokenId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"_userFeeRecipients","outputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"lpTokenId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"_userTokenIds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"lpTokenId","type":"uint256"}],"internalType":"struct LpMetaLocker.UserFeeRecipient","name":"recipient","type":"tuple"}],"name":"addUserFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"collectFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getLpTokenIdsForUser","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"id","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":"positionManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"newTeamRecipient","type":"address"},{"internalType":"uint256","name":"newTeamFee","type":"uint256"}],"name":"setOverrideTeamFeesForToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"lpTokenId","type":"uint256"}],"internalType":"struct LpMetaLocker.UserFeeRecipient[]","name":"recipients","type":"tuple[]"}],"name":"setUserFeeRecipients","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"updateClankerTeamFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newRecipient","type":"address"}],"name":"updateClankerTeamRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"withdrawERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"withdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]