编译器
0.8.17+commit.8df45f5f
文件 1 的 14:Caviar.sol
pragma solidity ^0.8.17;
import "solmate/auth/Owned.sol";
import "./lib/SafeERC20Namer.sol";
import "./Pair.sol";
contract Caviar is Owned {
using SafeERC20Namer for address;
mapping(address => mapping(address => mapping(bytes32 => address))) public pairs;
address public stolenNftFilterOracle;
event SetStolenNftFilterOracle(address indexed stolenNftFilterOracle);
event Create(address indexed nft, address indexed baseToken, bytes32 indexed merkleRoot);
event Destroy(address indexed nft, address indexed baseToken, bytes32 indexed merkleRoot);
constructor(address _stolenNftFilterOracle) Owned(msg.sender) {
stolenNftFilterOracle = _stolenNftFilterOracle;
}
function setStolenNftFilterOracle(address _stolenNftFilterOracle) public onlyOwner {
stolenNftFilterOracle = _stolenNftFilterOracle;
emit SetStolenNftFilterOracle(_stolenNftFilterOracle);
}
function create(address nft, address baseToken, bytes32 merkleRoot) public returns (Pair pair) {
require(pairs[nft][baseToken][merkleRoot] == address(0), "Pair already exists");
require(nft.code.length > 0, "Invalid NFT contract");
require(baseToken.code.length > 0 || baseToken == address(0), "Invalid base token contract");
string memory baseTokenSymbol = baseToken == address(0) ? "ETH" : baseToken.tokenSymbol();
string memory nftSymbol = nft.tokenSymbol();
string memory nftName = nft.tokenName();
string memory pairSymbol = string.concat(nftSymbol, ":", baseTokenSymbol);
pair = new Pair(nft, baseToken, merkleRoot, pairSymbol, nftName, nftSymbol);
pairs[nft][baseToken][merkleRoot] = address(pair);
emit Create(nft, baseToken, merkleRoot);
}
function destroy(address nft, address baseToken, bytes32 merkleRoot) public {
require(msg.sender == pairs[nft][baseToken][merkleRoot], "Only pair can destroy itself");
delete pairs[nft][baseToken][merkleRoot];
emit Destroy(nft, baseToken, merkleRoot);
}
}
文件 2 的 14:ERC20.sol
pragma solidity >=0.8.0;
abstract contract ERC20 {
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
string public name;
string public symbol;
uint8 public immutable decimals;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender];
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
文件 3 的 14:ERC721.sol
pragma solidity >=0.8.0;
abstract contract ERC721 {
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
string public name;
string public symbol;
function tokenURI(uint256 id) public view virtual returns (string memory);
mapping(uint256 => address) internal _ownerOf;
mapping(address => uint256) internal _balanceOf;
function ownerOf(uint256 id) public view virtual returns (address owner) {
require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
}
function balanceOf(address owner) public view virtual returns (uint256) {
require(owner != address(0), "ZERO_ADDRESS");
return _balanceOf[owner];
}
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
function approve(address spender, uint256 id) public virtual {
address owner = _ownerOf[id];
require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual {
require(from == _ownerOf[id], "WRONG_FROM");
require(to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
"NOT_AUTHORIZED"
);
unchecked {
_balanceOf[from]--;
_balanceOf[to]++;
}
_ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(
address from,
address to,
uint256 id
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
bytes calldata data
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 ||
interfaceId == 0x80ac58cd ||
interfaceId == 0x5b5e139f;
}
function _mint(address to, uint256 id) internal virtual {
require(to != address(0), "INVALID_RECIPIENT");
require(_ownerOf[id] == address(0), "ALREADY_MINTED");
unchecked {
_balanceOf[to]++;
}
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint256 id) internal virtual {
address owner = _ownerOf[id];
require(owner != address(0), "NOT_MINTED");
unchecked {
_balanceOf[owner]--;
}
delete _ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
function _safeMint(address to, uint256 id) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _safeMint(
address to,
uint256 id,
bytes memory data
) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
abstract contract ERC721TokenReceiver {
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
}
文件 4 的 14:FixedPointMathLib.sol
pragma solidity >=0.8.0;
library FixedPointMathLib {
uint256 internal constant MAX_UINT256 = 2**256 - 1;
uint256 internal constant WAD = 1e18;
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD);
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD);
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y);
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y);
}
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
assembly {
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
z := div(mul(x, y), denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
assembly {
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
assembly {
switch x
case 0 {
switch n
case 0 {
z := scalar
}
default {
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
z := scalar
}
default {
z := x
}
let half := shr(1, scalar)
for {
n := shr(1, n)
} n {
n := shr(1, n)
} {
if shr(128, x) {
revert(0, 0)
}
let xx := mul(x, x)
let xxRound := add(xx, half)
if lt(xxRound, xx) {
revert(0, 0)
}
x := div(xxRound, scalar)
if mod(n, 2) {
let zx := mul(z, x)
if iszero(eq(div(zx, x), z)) {
if iszero(iszero(x)) {
revert(0, 0)
}
}
let zxRound := add(zx, half)
if lt(zxRound, zx) {
revert(0, 0)
}
z := div(zxRound, scalar)
}
}
}
}
}
function sqrt(uint256 x) internal pure returns (uint256 z) {
assembly {
let y := x
z := 181
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
z := shr(18, mul(z, add(y, 65536)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := sub(z, lt(div(x, z), z))
}
}
function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := mod(x, y)
}
}
function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
assembly {
r := div(x, y)
}
}
function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := add(gt(mod(x, y), 0), div(x, y))
}
}
}
文件 5 的 14:LpToken.sol
pragma solidity ^0.8.17;
import "solmate/auth/Owned.sol";
import "solmate/tokens/ERC20.sol";
contract LpToken is Owned, ERC20 {
constructor(string memory pairSymbol)
Owned(msg.sender)
ERC20(string.concat(pairSymbol, " LP token"), string.concat("LP-", pairSymbol), 18)
{}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
function burn(address from, uint256 amount) public onlyOwner {
_burn(from, amount);
}
}
文件 6 的 14:Math.sol
pragma solidity ^0.8.0;
library Math {
enum Rounding {
Down,
Up,
Zero
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
uint256 prod0;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
require(denominator > prod1);
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (~denominator + 1);
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inverse = (3 * denominator) ^ 2;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
result = prod0 * inverse;
return result;
}
}
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 result = 1 << (log2(a) >> 1);
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10**64) {
value /= 10**64;
result += 64;
}
if (value >= 10**32) {
value /= 10**32;
result += 32;
}
if (value >= 10**16) {
value /= 10**16;
result += 16;
}
if (value >= 10**8) {
value /= 10**8;
result += 8;
}
if (value >= 10**4) {
value /= 10**4;
result += 4;
}
if (value >= 10**2) {
value /= 10**2;
result += 2;
}
if (value >= 10**1) {
result += 1;
}
}
return result;
}
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
}
}
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
文件 7 的 14:MerkleProofLib.sol
pragma solidity >=0.8.0;
library MerkleProofLib {
function verify(
bytes32[] calldata proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool isValid) {
assembly {
if proof.length {
let end := add(proof.offset, shl(5, proof.length))
let offset := proof.offset
for {} 1 {} {
let leafSlot := shl(5, gt(leaf, calldataload(offset)))
mstore(leafSlot, leaf)
mstore(xor(leafSlot, 32), calldataload(offset))
leaf := keccak256(0, 64)
offset := add(offset, 32)
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root)
}
}
}
文件 8 的 14:Owned.sol
pragma solidity >=0.8.0;
abstract contract Owned {
event OwnershipTransferred(address indexed user, address indexed newOwner);
address public owner;
modifier onlyOwner() virtual {
require(msg.sender == owner, "UNAUTHORIZED");
_;
}
constructor(address _owner) {
owner = _owner;
emit OwnershipTransferred(address(0), _owner);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}
文件 9 的 14:Pair.sol
pragma solidity ^0.8.17;
import "solmate/tokens/ERC20.sol";
import "solmate/tokens/ERC721.sol";
import "solmate/utils/MerkleProofLib.sol";
import "solmate/utils/SafeTransferLib.sol";
import "solmate/utils/FixedPointMathLib.sol";
import "openzeppelin/utils/math/Math.sol";
import "reservoir-oracle/ReservoirOracle.sol";
import "./LpToken.sol";
import "./Caviar.sol";
import "./StolenNftFilterOracle.sol";
contract Pair is ERC20, ERC721TokenReceiver {
using SafeTransferLib for address;
using SafeTransferLib for ERC20;
uint256 public constant CLOSE_GRACE_PERIOD = 7 days;
uint256 private constant ONE = 1e18;
uint256 private constant MINIMUM_LIQUIDITY = 100_000;
address public immutable nft;
address public immutable baseToken;
bytes32 public immutable merkleRoot;
LpToken public immutable lpToken;
Caviar public immutable caviar;
uint256 public closeTimestamp;
event Add(uint256 indexed baseTokenAmount, uint256 indexed fractionalTokenAmount, uint256 indexed lpTokenAmount);
event Remove(uint256 indexed baseTokenAmount, uint256 indexed fractionalTokenAmount, uint256 indexed lpTokenAmount);
event Buy(uint256 indexed inputAmount, uint256 indexed outputAmount);
event Sell(uint256 indexed inputAmount, uint256 indexed outputAmount);
event Wrap(uint256[] indexed tokenIds);
event Unwrap(uint256[] indexed tokenIds);
event Close(uint256 indexed closeTimestamp);
event Withdraw(uint256 indexed tokenId);
constructor(
address _nft,
address _baseToken,
bytes32 _merkleRoot,
string memory pairSymbol,
string memory nftName,
string memory nftSymbol
) ERC20(string.concat(nftName, " fractional token"), string.concat("f", nftSymbol), 18) {
nft = _nft;
baseToken = _baseToken;
merkleRoot = _merkleRoot;
lpToken = new LpToken(pairSymbol);
caviar = Caviar(msg.sender);
}
function add(
uint256 baseTokenAmount,
uint256 fractionalTokenAmount,
uint256 minLpTokenAmount,
uint256 minPrice,
uint256 maxPrice,
uint256 deadline
) public payable returns (uint256 lpTokenAmount) {
require(deadline == 0 || deadline >= block.timestamp, "Expired");
require(baseTokenAmount > 0 && fractionalTokenAmount > 0, "Input token amount is zero");
require(baseToken == address(0) ? msg.value == baseTokenAmount : msg.value == 0, "Invalid ether input");
uint256 lpTokenSupply = lpToken.totalSupply();
if (lpTokenSupply != 0) {
uint256 _price = price();
require(_price >= minPrice && _price <= maxPrice, "Slippage: price out of bounds");
}
lpTokenAmount = addQuote(baseTokenAmount, fractionalTokenAmount, lpTokenSupply);
require(lpTokenAmount >= minLpTokenAmount, "Slippage: lp token amount out");
_transferFrom(msg.sender, address(this), fractionalTokenAmount);
lpToken.mint(msg.sender, lpTokenAmount);
if (lpTokenSupply == 0) {
lpToken.mint(caviar.owner(), MINIMUM_LIQUIDITY);
}
if (baseToken != address(0)) {
ERC20(baseToken).safeTransferFrom(msg.sender, address(this), baseTokenAmount);
}
emit Add(baseTokenAmount, fractionalTokenAmount, lpTokenAmount);
}
function remove(
uint256 lpTokenAmount,
uint256 minBaseTokenOutputAmount,
uint256 minFractionalTokenOutputAmount,
uint256 deadline
) public returns (uint256 baseTokenOutputAmount, uint256 fractionalTokenOutputAmount) {
require(deadline == 0 || deadline >= block.timestamp, "Expired");
(baseTokenOutputAmount, fractionalTokenOutputAmount) = removeQuote(lpTokenAmount);
require(baseTokenOutputAmount >= minBaseTokenOutputAmount, "Slippage: base token amount out");
require(fractionalTokenOutputAmount >= minFractionalTokenOutputAmount, "Slippage: fractional token out");
_transferFrom(address(this), msg.sender, fractionalTokenOutputAmount);
lpToken.burn(msg.sender, lpTokenAmount);
if (baseToken == address(0)) {
msg.sender.safeTransferETH(baseTokenOutputAmount);
} else {
ERC20(baseToken).safeTransfer(msg.sender, baseTokenOutputAmount);
}
emit Remove(baseTokenOutputAmount, fractionalTokenOutputAmount, lpTokenAmount);
}
function buy(uint256 outputAmount, uint256 maxInputAmount, uint256 deadline)
public
payable
returns (uint256 inputAmount)
{
require(deadline == 0 || deadline >= block.timestamp, "Expired");
require(baseToken == address(0) ? msg.value == maxInputAmount : msg.value == 0, "Invalid ether input");
inputAmount = buyQuote(outputAmount);
require(inputAmount <= maxInputAmount, "Slippage: amount in");
_transferFrom(address(this), msg.sender, outputAmount);
if (baseToken == address(0)) {
uint256 refundAmount = maxInputAmount - inputAmount;
if (refundAmount > 0) msg.sender.safeTransferETH(refundAmount);
} else {
ERC20(baseToken).safeTransferFrom(msg.sender, address(this), inputAmount);
}
emit Buy(inputAmount, outputAmount);
}
function sell(uint256 inputAmount, uint256 minOutputAmount, uint256 deadline)
public
returns (uint256 outputAmount)
{
require(deadline == 0 || deadline >= block.timestamp, "Expired");
outputAmount = sellQuote(inputAmount);
require(outputAmount >= minOutputAmount, "Slippage: amount out");
_transferFrom(msg.sender, address(this), inputAmount);
if (baseToken == address(0)) {
msg.sender.safeTransferETH(outputAmount);
} else {
ERC20(baseToken).safeTransfer(msg.sender, outputAmount);
}
emit Sell(inputAmount, outputAmount);
}
function wrap(uint256[] calldata tokenIds, bytes32[][] calldata proofs, ReservoirOracle.Message[] calldata messages)
public
returns (uint256 fractionalTokenAmount)
{
require(closeTimestamp == 0, "Wrap: closed");
_validateTokenIds(tokenIds, proofs);
_validateTokensAreNotStolen(tokenIds, messages);
fractionalTokenAmount = tokenIds.length * ONE;
_mint(msg.sender, fractionalTokenAmount);
for (uint256 i = 0; i < tokenIds.length;) {
ERC721(nft).safeTransferFrom(msg.sender, address(this), tokenIds[i]);
unchecked {
i++;
}
}
emit Wrap(tokenIds);
}
function unwrap(uint256[] calldata tokenIds, bool withFee) public returns (uint256 fractionalTokenAmount) {
fractionalTokenAmount = tokenIds.length * ONE;
_burn(msg.sender, fractionalTokenAmount);
if (withFee) {
uint256 fee = fractionalTokenAmount * 3 / 1000;
_transferFrom(msg.sender, address(this), fee);
fractionalTokenAmount += fee;
}
for (uint256 i = 0; i < tokenIds.length;) {
ERC721(nft).safeTransferFrom(address(this), msg.sender, tokenIds[i]);
unchecked {
i++;
}
}
emit Unwrap(tokenIds);
}
function nftAdd(
uint256 baseTokenAmount,
uint256[] calldata tokenIds,
uint256 minLpTokenAmount,
uint256 minPrice,
uint256 maxPrice,
uint256 deadline,
bytes32[][] calldata proofs,
ReservoirOracle.Message[] calldata messages
) public payable returns (uint256 lpTokenAmount) {
uint256 fractionalTokenAmount = wrap(tokenIds, proofs, messages);
lpTokenAmount = add(baseTokenAmount, fractionalTokenAmount, minLpTokenAmount, minPrice, maxPrice, deadline);
}
function nftRemove(
uint256 lpTokenAmount,
uint256 minBaseTokenOutputAmount,
uint256 deadline,
uint256[] calldata tokenIds,
bool withFee
) public returns (uint256 baseTokenOutputAmount, uint256 fractionalTokenOutputAmount) {
(baseTokenOutputAmount, fractionalTokenOutputAmount) =
remove(lpTokenAmount, minBaseTokenOutputAmount, tokenIds.length * ONE, deadline);
unwrap(tokenIds, withFee);
}
function nftBuy(uint256[] calldata tokenIds, uint256 maxInputAmount, uint256 deadline)
public
payable
returns (uint256 inputAmount)
{
inputAmount = buy(tokenIds.length * ONE, maxInputAmount, deadline);
unwrap(tokenIds, false);
}
function nftSell(
uint256[] calldata tokenIds,
uint256 minOutputAmount,
uint256 deadline,
bytes32[][] calldata proofs,
ReservoirOracle.Message[] calldata messages
) public returns (uint256 outputAmount) {
uint256 inputAmount = wrap(tokenIds, proofs, messages);
outputAmount = sell(inputAmount, minOutputAmount, deadline);
}
function close() public {
require(caviar.owner() == msg.sender, "Close: not owner");
closeTimestamp = block.timestamp + CLOSE_GRACE_PERIOD;
caviar.destroy(nft, baseToken, merkleRoot);
emit Close(closeTimestamp);
}
function withdraw(uint256 tokenId) public {
require(caviar.owner() == msg.sender, "Withdraw: not owner");
require(closeTimestamp != 0, "Withdraw not initiated");
require(block.timestamp >= closeTimestamp, "Not withdrawable yet");
ERC721(nft).safeTransferFrom(address(this), msg.sender, tokenId);
emit Withdraw(tokenId);
}
function baseTokenReserves() public view returns (uint256) {
return _baseTokenReserves();
}
function fractionalTokenReserves() public view returns (uint256) {
return balanceOf[address(this)];
}
function price() public view returns (uint256) {
uint256 exponent = baseToken == address(0) ? 18 : (36 - ERC20(baseToken).decimals());
return (_baseTokenReserves() * 10 ** exponent) / fractionalTokenReserves();
}
function buyQuote(uint256 outputAmount) public view returns (uint256) {
return FixedPointMathLib.mulDivUp(
outputAmount * 1000, baseTokenReserves(), (fractionalTokenReserves() - outputAmount) * 990
);
}
function sellQuote(uint256 inputAmount) public view returns (uint256) {
uint256 inputAmountWithFee = inputAmount * 990;
return (inputAmountWithFee * baseTokenReserves()) / ((fractionalTokenReserves() * 1000) + inputAmountWithFee);
}
function addQuote(uint256 baseTokenAmount, uint256 fractionalTokenAmount, uint256 lpTokenSupply)
public
view
returns (uint256)
{
if (lpTokenSupply != 0) {
uint256 baseTokenShare = (baseTokenAmount * lpTokenSupply) / baseTokenReserves();
uint256 fractionalTokenShare = (fractionalTokenAmount * lpTokenSupply) / fractionalTokenReserves();
return Math.min(baseTokenShare, fractionalTokenShare);
} else {
return Math.sqrt(baseTokenAmount * fractionalTokenAmount) - MINIMUM_LIQUIDITY;
}
}
function removeQuote(uint256 lpTokenAmount) public view returns (uint256, uint256) {
uint256 lpTokenSupply = lpToken.totalSupply();
uint256 baseTokenOutputAmount = (baseTokenReserves() * lpTokenAmount) / lpTokenSupply;
uint256 fractionalTokenOutputAmount = (fractionalTokenReserves() * lpTokenAmount) / lpTokenSupply;
uint256 upperFractionalTokenOutputAmount = (fractionalTokenReserves() * (lpTokenAmount + 1)) / lpTokenSupply;
if (
fractionalTokenOutputAmount % 1e18 != 0
&& upperFractionalTokenOutputAmount - fractionalTokenOutputAmount <= 1000 && lpTokenSupply > 1e15
) {
fractionalTokenOutputAmount = upperFractionalTokenOutputAmount;
}
return (baseTokenOutputAmount, fractionalTokenOutputAmount);
}
function _transferFrom(address from, address to, uint256 amount) internal returns (bool) {
balanceOf[from] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
function _validateTokensAreNotStolen(uint256[] calldata tokenIds, ReservoirOracle.Message[] calldata messages)
internal
view
{
address stolenNftFilterAddress = caviar.stolenNftFilterOracle();
if (stolenNftFilterAddress == address(0)) return;
StolenNftFilterOracle(stolenNftFilterAddress).validateTokensAreNotStolen(nft, tokenIds, messages);
}
function _validateTokenIds(uint256[] calldata tokenIds, bytes32[][] calldata proofs) internal view {
if (merkleRoot == bytes32(0)) return;
for (uint256 i = 0; i < tokenIds.length;) {
bool isValid = MerkleProofLib.verify(
proofs[i],
merkleRoot,
keccak256(bytes.concat(keccak256(abi.encode(tokenIds[i]))))
);
require(isValid, "Invalid merkle proof");
unchecked {
i++;
}
}
}
function _baseTokenReserves() internal view returns (uint256) {
return baseToken == address(0)
? address(this).balance - msg.value
: ERC20(baseToken).balanceOf(address(this));
}
}
文件 10 的 14:ReservoirOracle.sol
pragma solidity ^0.8.13;
abstract contract ReservoirOracle {
struct Message {
bytes32 id;
bytes payload;
uint256 timestamp;
bytes signature;
}
error InvalidMessage();
address public RESERVOIR_ORACLE_ADDRESS;
constructor(address reservoirOracleAddress) {
RESERVOIR_ORACLE_ADDRESS = reservoirOracleAddress;
}
function updateReservoirOracleAddress(address newReservoirOracleAddress)
public
virtual;
function _verifyMessage(
bytes32 id,
uint256 validFor,
Message memory message
) internal view virtual returns (bool success) {
if (id != message.id) {
return false;
}
if (
message.timestamp > block.timestamp ||
message.timestamp + validFor < block.timestamp
) {
return false;
}
bytes32 r;
bytes32 s;
uint8 v;
bytes memory signature = message.signature;
if (signature.length == 64) {
bytes32 vs;
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
s := and(
vs,
0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
)
v := add(shr(255, vs), 27)
}
} else if (signature.length == 65) {
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
} else {
return false;
}
address signerAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n32",
keccak256(
abi.encode(
keccak256(
"Message(bytes32 id,bytes payload,uint256 timestamp)"
),
message.id,
keccak256(message.payload),
message.timestamp
)
)
)
),
v,
r,
s
);
return signerAddress == RESERVOIR_ORACLE_ADDRESS;
}
}
文件 11 的 14:SafeERC20Namer.sol
pragma solidity ^0.8.17;
import "openzeppelin/utils/Strings.sol";
library SafeERC20Namer {
function bytes32ToString(bytes32 x) private pure returns (string memory) {
bytes memory bytesString = new bytes(32);
uint256 charCount = 0;
for (uint256 j = 0; j < 32; j++) {
bytes1 char = x[j];
if (char != 0) {
bytesString[charCount] = char;
charCount++;
}
}
bytes memory bytesStringTrimmed = new bytes(charCount);
for (uint256 j = 0; j < charCount; j++) {
bytesStringTrimmed[j] = bytesString[j];
}
return string(bytesStringTrimmed);
}
function addressToName(address token) private pure returns (string memory) {
return Strings.toHexString(uint160(token));
}
function addressToSymbol(address token) private pure returns (string memory) {
return Strings.toHexString(uint160(token) >> (160 - 4 * 4));
}
function callAndParseStringReturn(address token, bytes4 selector) private view returns (string memory) {
(bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(selector));
if (!success || data.length == 0) {
return "";
}
if (data.length == 32) {
bytes32 decoded = abi.decode(data, (bytes32));
return bytes32ToString(decoded);
} else if (data.length > 64) {
return abi.decode(data, (string));
}
return "";
}
function tokenSymbol(address token) internal view returns (string memory) {
string memory symbol = callAndParseStringReturn(token, 0x95d89b41);
if (bytes(symbol).length == 0) {
return addressToSymbol(token);
}
return symbol;
}
function tokenName(address token) internal view returns (string memory) {
string memory name = callAndParseStringReturn(token, 0x06fdde03);
if (bytes(name).length == 0) {
return addressToName(token);
}
return name;
}
}
文件 12 的 14:SafeTransferLib.sol
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
library SafeTransferLib {
function safeTransferETH(address to, uint256 amount) internal {
bool success;
assembly {
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), from)
mstore(add(freeMemoryPointer, 36), to)
mstore(add(freeMemoryPointer, 68), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to)
mstore(add(freeMemoryPointer, 36), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to)
mstore(add(freeMemoryPointer, 36), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
文件 13 的 14:StolenNftFilterOracle.sol
pragma solidity ^0.8.17;
import "solmate/auth/Owned.sol";
import "reservoir-oracle/ReservoirOracle.sol";
contract StolenNftFilterOracle is ReservoirOracle, Owned {
bytes32 private constant TOKEN_TYPE_HASH = keccak256("Token(address contract,uint256 tokenId)");
uint256 public cooldownPeriod = 0;
uint256 public validFor = 60 minutes;
constructor() Owned(msg.sender) ReservoirOracle(0xAeB1D03929bF87F69888f381e73FBf75753d75AF) {}
function setCooldownPeriod(uint256 _cooldownPeriod) public onlyOwner {
cooldownPeriod = _cooldownPeriod;
}
function setValidFor(uint256 _validFor) public onlyOwner {
validFor = _validFor;
}
function updateReservoirOracleAddress(address newReservoirOracleAddress) public override onlyOwner {
RESERVOIR_ORACLE_ADDRESS = newReservoirOracleAddress;
}
function validateTokensAreNotStolen(address tokenAddress, uint256[] calldata tokenIds, Message[] calldata messages)
public
view
{
for (uint256 i = 0; i < tokenIds.length; i++) {
Message calldata message = messages[i];
bytes32 expectedMessageId = keccak256(abi.encode(TOKEN_TYPE_HASH, tokenAddress, tokenIds[i]));
require(_verifyMessage(expectedMessageId, validFor, message), "Message has invalid signature");
(bool isFlagged, uint256 lastTransferTime) = abi.decode(message.payload, (bool, uint256));
require(!isFlagged, "NFT is flagged as suspicious");
require(lastTransferTime + cooldownPeriod < block.timestamp, "NFT was transferred too recently");
}
}
}
文件 14 的 14:Strings.sol
pragma solidity ^0.8.0;
import "./math/Math.sol";
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
{
"compilationTarget": {
"src/Pair.sol": "Pair"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 100
},
"remappings": [
":@manifoldxyz/libraries-solidity/=lib/royalty-registry-solidity/lib/libraries-solidity/",
":@openzeppelin/contracts-upgradeable/=lib/royalty-registry-solidity/lib/openzeppelin-contracts-upgradeable/contracts/",
":@openzeppelin/contracts/=lib/royalty-registry-solidity/lib/openzeppelin-contracts/contracts/",
":ERC721A/=lib/ERC721A/contracts/",
":create2-helpers/=lib/royalty-registry-solidity/lib/create2-helpers/src/",
":create2-scripts/=lib/royalty-registry-solidity/lib/create2-helpers/script/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/",
":libraries-solidity/=lib/royalty-registry-solidity/lib/libraries-solidity/contracts/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
":openzeppelin/=lib/openzeppelin-contracts/contracts/",
":oracle/=lib/oracle/contracts/",
":reservoir-oracle/=lib/oracle/contracts/",
":royalty-registry-solidity/=lib/royalty-registry-solidity/contracts/",
":solmate/=lib/solmate/src/"
]
}
[{"inputs":[{"internalType":"address","name":"_nft","type":"address"},{"internalType":"address","name":"_baseToken","type":"address"},{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"},{"internalType":"string","name":"pairSymbol","type":"string"},{"internalType":"string","name":"nftName","type":"string"},{"internalType":"string","name":"nftSymbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"baseTokenAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"fractionalTokenAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"lpTokenAmount","type":"uint256"}],"name":"Add","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"inputAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"outputAmount","type":"uint256"}],"name":"Buy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"closeTimestamp","type":"uint256"}],"name":"Close","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"baseTokenAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"fractionalTokenAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"lpTokenAmount","type":"uint256"}],"name":"Remove","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"inputAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"outputAmount","type":"uint256"}],"name":"Sell","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"Unwrap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"Wrap","type":"event"},{"inputs":[],"name":"CLOSE_GRACE_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"baseTokenAmount","type":"uint256"},{"internalType":"uint256","name":"fractionalTokenAmount","type":"uint256"},{"internalType":"uint256","name":"minLpTokenAmount","type":"uint256"},{"internalType":"uint256","name":"minPrice","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"add","outputs":[{"internalType":"uint256","name":"lpTokenAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"baseTokenAmount","type":"uint256"},{"internalType":"uint256","name":"fractionalTokenAmount","type":"uint256"},{"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"addQuote","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseTokenReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"outputAmount","type":"uint256"},{"internalType":"uint256","name":"maxInputAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"buy","outputs":[{"internalType":"uint256","name":"inputAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"outputAmount","type":"uint256"}],"name":"buyQuote","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"caviar","outputs":[{"internalType":"contract Caviar","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"close","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"closeTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fractionalTokenReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lpToken","outputs":[{"internalType":"contract LpToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"merkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nft","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"baseTokenAmount","type":"uint256"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256","name":"minLpTokenAmount","type":"uint256"},{"internalType":"uint256","name":"minPrice","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes32[][]","name":"proofs","type":"bytes32[][]"},{"components":[{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct ReservoirOracle.Message[]","name":"messages","type":"tuple[]"}],"name":"nftAdd","outputs":[{"internalType":"uint256","name":"lpTokenAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256","name":"maxInputAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"nftBuy","outputs":[{"internalType":"uint256","name":"inputAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"lpTokenAmount","type":"uint256"},{"internalType":"uint256","name":"minBaseTokenOutputAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"bool","name":"withFee","type":"bool"}],"name":"nftRemove","outputs":[{"internalType":"uint256","name":"baseTokenOutputAmount","type":"uint256"},{"internalType":"uint256","name":"fractionalTokenOutputAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256","name":"minOutputAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes32[][]","name":"proofs","type":"bytes32[][]"},{"components":[{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct ReservoirOracle.Message[]","name":"messages","type":"tuple[]"}],"name":"nftSell","outputs":[{"internalType":"uint256","name":"outputAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"price","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"lpTokenAmount","type":"uint256"},{"internalType":"uint256","name":"minBaseTokenOutputAmount","type":"uint256"},{"internalType":"uint256","name":"minFractionalTokenOutputAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"remove","outputs":[{"internalType":"uint256","name":"baseTokenOutputAmount","type":"uint256"},{"internalType":"uint256","name":"fractionalTokenOutputAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"lpTokenAmount","type":"uint256"}],"name":"removeQuote","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"inputAmount","type":"uint256"},{"internalType":"uint256","name":"minOutputAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"sell","outputs":[{"internalType":"uint256","name":"outputAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"inputAmount","type":"uint256"}],"name":"sellQuote","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"bool","name":"withFee","type":"bool"}],"name":"unwrap","outputs":[{"internalType":"uint256","name":"fractionalTokenAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"bytes32[][]","name":"proofs","type":"bytes32[][]"},{"components":[{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct ReservoirOracle.Message[]","name":"messages","type":"tuple[]"}],"name":"wrap","outputs":[{"internalType":"uint256","name":"fractionalTokenAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]