Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 3,073 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Buy | 21044995 | 28 days ago | IN | 0 ETH | 0.0001416 | ||||
Buy | 20815598 | 60 days ago | IN | 0 ETH | 0.00068189 | ||||
Buy | 20661070 | 81 days ago | IN | 0 ETH | 0.00002605 | ||||
Buy | 20651873 | 82 days ago | IN | 0 ETH | 0.00002637 | ||||
Buy | 20570339 | 94 days ago | IN | 0 ETH | 0.00008984 | ||||
Buy | 20547390 | 97 days ago | IN | 0 ETH | 0.00002277 | ||||
Buy | 20487343 | 105 days ago | IN | 0 ETH | 0.00007035 | ||||
Sell | 20476622 | 107 days ago | IN | 0 ETH | 0.00059364 | ||||
Buy | 20474586 | 107 days ago | IN | 0.16242713 ETH | 0.00023704 | ||||
Buy | 20468280 | 108 days ago | IN | 0.15189139 ETH | 0.00052176 | ||||
Buy | 20460742 | 109 days ago | IN | 0.14207868 ETH | 0.00967881 | ||||
Buy | 20460495 | 109 days ago | IN | 0 ETH | 0.0010112 | ||||
Buy | 20459932 | 109 days ago | IN | 0.1344704 ETH | 0.00662535 | ||||
Sell | 20458805 | 109 days ago | IN | 0 ETH | 0.00104296 | ||||
Sell | 20458485 | 109 days ago | IN | 0 ETH | 0.00018127 | ||||
Buy | 20457674 | 110 days ago | IN | 0.05280504 ETH | 0.00046213 | ||||
Buy | 20454388 | 110 days ago | IN | 0.02480462 ETH | 0.00036089 | ||||
Buy | 20454385 | 110 days ago | IN | 0.02480462 ETH | 0.00022635 | ||||
Buy | 20447740 | 111 days ago | IN | 0.15141151 ETH | 0.00015249 | ||||
Buy | 20445432 | 111 days ago | IN | 0.02635095 ETH | 0.00023306 | ||||
Sell | 20443301 | 112 days ago | IN | 0 ETH | 0.00112229 | ||||
Sell | 20443238 | 112 days ago | IN | 0 ETH | 0.00103124 | ||||
Sell | 20440013 | 112 days ago | IN | 0 ETH | 0.00051347 | ||||
Change | 20436345 | 113 days ago | IN | 0.00016231 ETH | 0.00148043 | ||||
Sell | 20434292 | 113 days ago | IN | 0 ETH | 0.00242485 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
20476622 | 107 days ago | 0.0964081 ETH | ||||
20476622 | 107 days ago | 0.0964081 ETH | ||||
20474586 | 107 days ago | 0.16242713 ETH | ||||
20468280 | 108 days ago | 0.15189139 ETH | ||||
20460742 | 109 days ago | 0.14207868 ETH | ||||
20459932 | 109 days ago | 0.00002228 ETH | ||||
20459932 | 109 days ago | 0.00002228 ETH | ||||
20459932 | 109 days ago | 0.1344704 ETH | ||||
20458805 | 109 days ago | 0.11197683 ETH | ||||
20458805 | 109 days ago | 0.05518556 ETH | ||||
20458805 | 109 days ago | 0.05679126 ETH | ||||
20458485 | 109 days ago | 0.05711657 ETH | ||||
20458485 | 109 days ago | 0.05711657 ETH | ||||
20457674 | 110 days ago | 0.05280504 ETH | ||||
20454388 | 110 days ago | 0.02480462 ETH | ||||
20454385 | 110 days ago | 0.02480462 ETH | ||||
20447740 | 111 days ago | 0.15141151 ETH | ||||
20445432 | 111 days ago | 0.00000236 ETH | ||||
20445432 | 111 days ago | 0.00000236 ETH | ||||
20445432 | 111 days ago | 0.02635095 ETH | ||||
20443301 | 112 days ago | 0.03474442 ETH | ||||
20443301 | 112 days ago | 0.03474442 ETH | ||||
20443238 | 112 days ago | 0.03047789 ETH | ||||
20443238 | 112 days ago | 0.03047789 ETH | ||||
20440013 | 112 days ago | 0.05915074 ETH |
Loading...
Loading
Contract Name:
EthRouter
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; /* * _H_ * /___\ * \888/ * ~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~U~^~^~^~^~^~^~^ * ~ | * ~ o | ~ * ___ o | * _,.--,.'` `~'-.._ O | * /_ .-" _ /_\'. | ~ * .-';' (( ` \0/ `\ # * /__; ((_ ,_ | , # * .-; \_ / # _#, * / ; .-' / _.--""-.\`~` `#(('\\ ~ * ;-'; / / .' )) \\ * ; /.--'.' (( )) * \ | ~ \\ (( * \ | )) ` * ~ \ | ` * \ | * .` `""-. * .' \ ~ ~ * | |\ | * \ / '-._| * \.' */ import {ERC721, ERC721TokenReceiver} from "solmate/tokens/ERC721.sol"; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {IERC2981} from "openzeppelin/interfaces/IERC2981.sol"; import {Pair, ReservoirOracle} from "caviar/Pair.sol"; import {IRoyaltyRegistry} from "royalty-registry-solidity/IRoyaltyRegistry.sol"; import {PrivatePool} from "./PrivatePool.sol"; import {IStolenNftOracle} from "./interfaces/IStolenNftOracle.sol"; /// @title Eth Router /// @author out.eth (@outdoteth) /// @notice This contract is used to route buy, sell, and change orders to multiple pools in one transaction. It /// will route the orders to either a private pool or a public pool. If the order goes to a public pool, then users /// can choose whether or not they would like to pay royalties. The only base token which is supported is native ETH. contract EthRouter is ERC721TokenReceiver { using SafeTransferLib for address; using SafeTransferLib for address payable; struct Buy { address payable pool; uint256[] tokenIds; uint256[] tokenWeights; PrivatePool.MerkleMultiProof proof; uint256 baseTokenAmount; bool isPublicPool; } struct Sell { address payable pool; uint256[] tokenIds; uint256[] tokenWeights; PrivatePool.MerkleMultiProof proof; IStolenNftOracle.Message[] stolenNftProofs; bool isPublicPool; bytes32[][] publicPoolProofs; } struct Change { address payable pool; uint256[] inputTokenIds; uint256[] inputTokenWeights; PrivatePool.MerkleMultiProof inputProof; IStolenNftOracle.Message[] stolenNftProofs; uint256[] outputTokenIds; uint256[] outputTokenWeights; PrivatePool.MerkleMultiProof outputProof; uint256 baseTokenAmount; bool isPublicPool; } error DeadlinePassed(); error OutputAmountTooSmall(); error PriceOutOfRange(); error InvalidRoyaltyFee(); error MismatchedTokenIds(); address public immutable royaltyRegistry; receive() external payable {} constructor(address _royaltyRegistry) { royaltyRegistry = _royaltyRegistry; } /// @notice Executes a series of buy operations against public or private pools. /// @param buys The buy operations to execute. /// @param deadline The deadline for the transaction to be mined. Will revert if timestamp is greater than deadline. /// If it's set to 0 then there is no deadline. /// @param payRoyalties Whether to pay royalties or not. function buy(Buy[] calldata buys, uint256 deadline, bool payRoyalties) public payable { // check that the deadline has not passed (if any) if (block.timestamp > deadline && deadline != 0) { revert DeadlinePassed(); } // loop through and execute the the buys for (uint256 i = 0; i < buys.length; i++) { // fetch the nft address (PrivatePool and Pair both have an nft() method) address nft = PrivatePool(buys[i].pool).nft(); if (buys[i].isPublicPool) { // execute the buy against a public pool uint256 inputAmount = Pair(buys[i].pool).nftBuy{value: buys[i].baseTokenAmount}( buys[i].tokenIds, buys[i].baseTokenAmount, 0 ); // pay the royalties if buyer has opted-in if (payRoyalties) { uint256 salePrice = inputAmount / buys[i].tokenIds.length; for (uint256 j = 0; j < buys[i].tokenIds.length; j++) { // get the royalty fee and recipient (uint256 royaltyFee, address royaltyRecipient) = getRoyalty(nft, buys[i].tokenIds[j], salePrice); if (royaltyFee > 0 && royaltyRecipient != address(0)) { // transfer the royalty fee to the royalty recipient royaltyRecipient.safeTransferETH(royaltyFee); } } } } else { // execute the buy against a private pool PrivatePool(buys[i].pool).buy{value: buys[i].baseTokenAmount}( buys[i].tokenIds, buys[i].tokenWeights, buys[i].proof ); } for (uint256 j = 0; j < buys[i].tokenIds.length; j++) { // transfer the NFT to the caller ERC721(nft).safeTransferFrom(address(this), msg.sender, buys[i].tokenIds[j]); } } // refund any surplus ETH to the caller if (address(this).balance > 0) { msg.sender.safeTransferETH(address(this).balance); } } /// @notice Executes a series of sell operations against public or private pools. /// @param sells The sell operations to execute. /// @param minOutputAmount The minimum amount of output tokens that must be received for the transaction to succeed. /// @param deadline The deadline for the transaction to be mined. Will revert if timestamp is greater than deadline. /// Set to 0 for there to be no deadline. /// @param payRoyalties Whether to pay royalties or not. function sell(Sell[] calldata sells, uint256 minOutputAmount, uint256 deadline, bool payRoyalties) public { // check that the deadline has not passed (if any) if (block.timestamp > deadline && deadline != 0) { revert DeadlinePassed(); } // loop through and execute the sells for (uint256 i = 0; i < sells.length; i++) { // fetch the nft address (PrivatePool and Pair both have an nft() method) address nft = PrivatePool(sells[i].pool).nft(); // transfer the NFTs into the router from the caller for (uint256 j = 0; j < sells[i].tokenIds.length; j++) { ERC721(nft).safeTransferFrom(msg.sender, address(this), sells[i].tokenIds[j]); } // approve the pair to transfer NFTs from the router _approveNfts(nft, sells[i].pool); if (sells[i].isPublicPool) { // execute the sell against a public pool uint256 outputAmount = Pair(sells[i].pool).nftSell( sells[i].tokenIds, 0, 0, sells[i].publicPoolProofs, // ReservoirOracle.Message[] is the exact same as IStolenNftOracle.Message[] and can be // decoded/encoded 1-to-1. abi.decode(abi.encode(sells[i].stolenNftProofs), (ReservoirOracle.Message[])) ); // pay the royalties if seller has opted-in if (payRoyalties) { uint256 salePrice = outputAmount / sells[i].tokenIds.length; for (uint256 j = 0; j < sells[i].tokenIds.length; j++) { // get the royalty fee and recipient (uint256 royaltyFee, address royaltyRecipient) = getRoyalty(nft, sells[i].tokenIds[j], salePrice); if (royaltyFee > 0 && royaltyRecipient != address(0)) { // transfer the royalty fee to the royalty recipient royaltyRecipient.safeTransferETH(royaltyFee); } } } } else { // execute the sell against a private pool PrivatePool(sells[i].pool).sell( sells[i].tokenIds, sells[i].tokenWeights, sells[i].proof, sells[i].stolenNftProofs ); } } // check that the output amount is greater than the minimum if (address(this).balance < minOutputAmount) { revert OutputAmountTooSmall(); } // transfer the output amount to the caller msg.sender.safeTransferETH(address(this).balance); } /// @notice Executes a deposit to a private pool (transfers NFTs and ETH to the pool). /// @param privatePool The private pool to deposit to. /// @param nft The NFT contract address. /// @param tokenIds The token IDs of the NFTs to deposit. /// @param minPrice The minimum price of the pool. Will revert if price is smaller than this. /// @param maxPrice The maximum price of the pool. Will revert if price is greater than this. /// @param deadline The deadline for the transaction to be mined. Will revert if timestamp is greater than deadline. /// Set to 0 for deadline to be ignored. function deposit( address payable privatePool, address nft, uint256[] calldata tokenIds, uint256 minPrice, uint256 maxPrice, uint256 deadline ) public payable { // check deadline has not passed (if any) if (block.timestamp > deadline && deadline != 0) { revert DeadlinePassed(); } // check pool price is in between min and max uint256 price = PrivatePool(privatePool).price(); if (price > maxPrice || price < minPrice) { revert PriceOutOfRange(); } // transfer NFTs from caller for (uint256 i = 0; i < tokenIds.length; i++) { ERC721(nft).safeTransferFrom(msg.sender, address(this), tokenIds[i]); } // approve the pair to transfer NFTs from the router _approveNfts(nft, privatePool); // execute deposit PrivatePool(privatePool).deposit{value: msg.value}(tokenIds, msg.value); } /// @notice Executes a series of change operations against a private pool. /// @param changes The change operations to execute. /// @param deadline The deadline for the transaction to be mined. Will revert if timestamp is greater than deadline. /// Set to 0 for deadline to be ignored. function change(Change[] calldata changes, uint256 deadline) public payable { // check deadline has not passed (if any) if (block.timestamp > deadline && deadline != 0) { revert DeadlinePassed(); } // loop through and execute the changes for (uint256 i = 0; i < changes.length; i++) { Change memory _change = changes[i]; // fetch the nft address (PrivatePool and Pair both have an nft() method) address nft = PrivatePool(_change.pool).nft(); // transfer NFTs from caller for (uint256 j = 0; j < changes[i].inputTokenIds.length; j++) { ERC721(nft).safeTransferFrom(msg.sender, address(this), _change.inputTokenIds[j]); } // approve the pair to transfer NFTs from the router _approveNfts(nft, _change.pool); if (_change.isPublicPool) { // check that the input token ids length matches the output token ids length if (_change.inputTokenIds.length != _change.outputTokenIds.length) { revert MismatchedTokenIds(); } // empty proofs assumes that we only change against floor public pools bytes32[][] memory publicPoolProofs = new bytes32[][](0); // get some fractional tokens for the input tokens uint256 fractionalTokenAmount = Pair(_change.pool).wrap( _change.inputTokenIds, publicPoolProofs, // ReservoirOracle.Message[] is the exact same as IStolenNftOracle.Message[] and can be // decoded/encoded 1-to-1. abi.decode(abi.encode(_change.stolenNftProofs), (ReservoirOracle.Message[])) ); // buy the surplus fractional tokens required to pay the fee uint256 fractionalTokenFee = fractionalTokenAmount * 3 / 1000; Pair(_change.pool).buy{value: _change.baseTokenAmount}(fractionalTokenFee, _change.baseTokenAmount, 0); // exchange the fractional tokens for the target output tokens Pair(_change.pool).unwrap(_change.outputTokenIds, true); } else { // execute change PrivatePool(_change.pool).change{value: _change.baseTokenAmount}( _change.inputTokenIds, _change.inputTokenWeights, _change.inputProof, _change.stolenNftProofs, _change.outputTokenIds, _change.outputTokenWeights, _change.outputProof ); } // transfer NFTs to caller for (uint256 j = 0; j < changes[i].outputTokenIds.length; j++) { ERC721(nft).safeTransferFrom(address(this), msg.sender, _change.outputTokenIds[j]); } } // refund any surplus ETH to the caller if (address(this).balance > 0) { msg.sender.safeTransferETH(address(this).balance); } } /// @notice Gets the royalty and recipient for a given NFT and sale price. Looks up the royalty info from the /// manifold registry. /// @param nft The NFT contract address. /// @param tokenId The token ID of the NFT. /// @param salePrice The sale price of the NFT. /// @return royaltyFee The royalty fee to pay. /// @return recipient The address to pay the royalty fee to. function getRoyalty(address nft, uint256 tokenId, uint256 salePrice) public view returns (uint256 royaltyFee, address recipient) { // get the royalty lookup address address lookupAddress = IRoyaltyRegistry(royaltyRegistry).getRoyaltyLookupAddress(nft); if (IERC2981(lookupAddress).supportsInterface(type(IERC2981).interfaceId)) { // get the royalty fee from the registry (recipient, royaltyFee) = IERC2981(lookupAddress).royaltyInfo(tokenId, salePrice); // revert if the royalty fee is greater than the sale price if (royaltyFee > salePrice) revert InvalidRoyaltyFee(); } } function _approveNfts(address nft, address target) internal { // check if the router is already approved to transfer NFTs from the caller if (ERC721(nft).isApprovedForAll(address(this), target)) return; // approve the target to transfer NFTs from the router ERC721(nft).setApprovalForAll(target, true); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern, minimalist, and gas efficient ERC-721 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol) abstract contract ERC721 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ 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); /*////////////////////////////////////////////////////////////// METADATA STORAGE/LOGIC //////////////////////////////////////////////////////////////*/ string public name; string public symbol; function tokenURI(uint256 id) public view virtual returns (string memory); /*////////////////////////////////////////////////////////////// ERC721 BALANCE/OWNER STORAGE //////////////////////////////////////////////////////////////*/ 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]; } /*////////////////////////////////////////////////////////////// ERC721 APPROVAL STORAGE //////////////////////////////////////////////////////////////*/ mapping(uint256 => address) public getApproved; mapping(address => mapping(address => bool)) public isApprovedForAll; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(string memory _name, string memory _symbol) { name = _name; symbol = _symbol; } /*////////////////////////////////////////////////////////////// ERC721 LOGIC //////////////////////////////////////////////////////////////*/ 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" ); // Underflow of the sender's balance is impossible because we check for // ownership above and the recipient's balance can't realistically overflow. 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" ); } /*////////////////////////////////////////////////////////////// ERC165 LOGIC //////////////////////////////////////////////////////////////*/ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721 interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 id) internal virtual { require(to != address(0), "INVALID_RECIPIENT"); require(_ownerOf[id] == address(0), "ALREADY_MINTED"); // Counter overflow is incredibly unrealistic. 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"); // Ownership check above ensures no underflow. unchecked { _balanceOf[owner]--; } delete _ownerOf[id]; delete getApproved[id]; emit Transfer(owner, address(0), id); } /*////////////////////////////////////////////////////////////// INTERNAL SAFE MINT LOGIC //////////////////////////////////////////////////////////////*/ 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" ); } } /// @notice A generic interface for a contract which properly accepts ERC721 tokens. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol) abstract contract ERC721TokenReceiver { function onERC721Received( address, address, uint256, bytes calldata ) external virtual returns (bytes4) { return ERC721TokenReceiver.onERC721Received.selector; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { /*////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly assembly { // Transfer the ETH and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } require(success, "ETH_TRANSFER_FAILED"); } /*////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument. mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) ) } require(success, "TRANSFER_FROM_FAILED"); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "TRANSFER_FAILED"); } function safeApprove( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "APPROVE_FAILED"); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol) pragma solidity ^0.8.0; import "../utils/introspection/IERC165.sol"; /** * @dev Interface for the NFT Royalty Standard. * * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal * support for royalty payments across all NFT marketplaces and ecosystem participants. * * _Available since v4.5._ */ interface IERC2981 is IERC165 { /** * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of * exchange. The royalty amount is denominated and should be paid in that same unit of exchange. */ function royaltyInfo(uint256 tokenId, uint256 salePrice) external view returns (address receiver, uint256 royaltyAmount); }
// SPDX-License-Identifier: MIT 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"; /// @title Pair /// @author out.eth (@outdoteth) /// @notice A pair of an NFT and a base token that can be used to create and trade fractionalized NFTs. 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; // address(0) for ETH 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; // use address(0) for native ETH merkleRoot = _merkleRoot; lpToken = new LpToken(pairSymbol); caviar = Caviar(msg.sender); } // ************************ // // Core AMM logic // // *********************** // /// @notice Adds liquidity to the pair. /// @param baseTokenAmount The amount of base tokens to add. /// @param fractionalTokenAmount The amount of fractional tokens to add. /// @param minLpTokenAmount The minimum amount of LP tokens to mint. /// @param minPrice The minimum price that the pool should currently be at. /// @param maxPrice The maximum price that the pool should currently be at. /// @param deadline The deadline before the trade expires. /// @return lpTokenAmount The amount of LP tokens minted. function add( uint256 baseTokenAmount, uint256 fractionalTokenAmount, uint256 minLpTokenAmount, uint256 minPrice, uint256 maxPrice, uint256 deadline ) public payable returns (uint256 lpTokenAmount) { // *** Checks *** // // check that the trade has not expired require(deadline == 0 || deadline >= block.timestamp, "Expired"); // check the token amount inputs are not zero require(baseTokenAmount > 0 && fractionalTokenAmount > 0, "Input token amount is zero"); // check that correct eth input was sent - if the baseToken equals address(0) then native ETH is used require(baseToken == address(0) ? msg.value == baseTokenAmount : msg.value == 0, "Invalid ether input"); uint256 lpTokenSupply = lpToken.totalSupply(); // check that the price is within the bounds if there is liquidity in the pool if (lpTokenSupply != 0) { uint256 _price = price(); require(_price >= minPrice && _price <= maxPrice, "Slippage: price out of bounds"); } // calculate the lp token shares to mint lpTokenAmount = addQuote(baseTokenAmount, fractionalTokenAmount, lpTokenSupply); // check that the amount of lp tokens outputted is greater than the min amount require(lpTokenAmount >= minLpTokenAmount, "Slippage: lp token amount out"); // *** Effects *** // // transfer fractional tokens in _transferFrom(msg.sender, address(this), fractionalTokenAmount); // *** Interactions *** // // mint lp tokens to sender lpToken.mint(msg.sender, lpTokenAmount); // transfer first MINIMUM_LIQUIDITY lp tokens to the owner if (lpTokenSupply == 0) { lpToken.mint(caviar.owner(), MINIMUM_LIQUIDITY); } // transfer base tokens in if the base token is not ETH if (baseToken != address(0)) { // transfer base tokens in ERC20(baseToken).safeTransferFrom(msg.sender, address(this), baseTokenAmount); } emit Add(baseTokenAmount, fractionalTokenAmount, lpTokenAmount); } /// @notice Removes liquidity from the pair. /// @param lpTokenAmount The amount of LP tokens to burn. /// @param minBaseTokenOutputAmount The minimum amount of base tokens to receive. /// @param minFractionalTokenOutputAmount The minimum amount of fractional tokens to receive. /// @param deadline The deadline before the trade expires. /// @return baseTokenOutputAmount The amount of base tokens received. /// @return fractionalTokenOutputAmount The amount of fractional tokens received. function remove( uint256 lpTokenAmount, uint256 minBaseTokenOutputAmount, uint256 minFractionalTokenOutputAmount, uint256 deadline ) public returns (uint256 baseTokenOutputAmount, uint256 fractionalTokenOutputAmount) { // *** Checks *** // // check that the trade has not expired require(deadline == 0 || deadline >= block.timestamp, "Expired"); // calculate the output amounts (baseTokenOutputAmount, fractionalTokenOutputAmount) = removeQuote(lpTokenAmount); // check that the base token output amount is greater than the min amount require(baseTokenOutputAmount >= minBaseTokenOutputAmount, "Slippage: base token amount out"); // check that the fractional token output amount is greater than the min amount require(fractionalTokenOutputAmount >= minFractionalTokenOutputAmount, "Slippage: fractional token out"); // *** Effects *** // // transfer fractional tokens to sender _transferFrom(address(this), msg.sender, fractionalTokenOutputAmount); // *** Interactions *** // // burn lp tokens from sender lpToken.burn(msg.sender, lpTokenAmount); if (baseToken == address(0)) { // if base token is native ETH then send ether to sender msg.sender.safeTransferETH(baseTokenOutputAmount); } else { // transfer base tokens to sender ERC20(baseToken).safeTransfer(msg.sender, baseTokenOutputAmount); } emit Remove(baseTokenOutputAmount, fractionalTokenOutputAmount, lpTokenAmount); } /// @notice Buys fractional tokens from the pair. /// @param outputAmount The amount of fractional tokens to buy. /// @param maxInputAmount The maximum amount of base tokens to spend. /// @param deadline The deadline before the trade expires. /// @return inputAmount The amount of base tokens spent. function buy(uint256 outputAmount, uint256 maxInputAmount, uint256 deadline) public payable returns (uint256 inputAmount) { // *** Checks *** // // check that the trade has not expired require(deadline == 0 || deadline >= block.timestamp, "Expired"); // check that correct eth input was sent - if the baseToken equals address(0) then native ETH is used require(baseToken == address(0) ? msg.value == maxInputAmount : msg.value == 0, "Invalid ether input"); // calculate required input amount using xyk invariant inputAmount = buyQuote(outputAmount); // check that the required amount of base tokens is less than the max amount require(inputAmount <= maxInputAmount, "Slippage: amount in"); // *** Effects *** // // transfer fractional tokens to sender _transferFrom(address(this), msg.sender, outputAmount); // *** Interactions *** // if (baseToken == address(0)) { // refund surplus eth uint256 refundAmount = maxInputAmount - inputAmount; if (refundAmount > 0) msg.sender.safeTransferETH(refundAmount); } else { // transfer base tokens in ERC20(baseToken).safeTransferFrom(msg.sender, address(this), inputAmount); } emit Buy(inputAmount, outputAmount); } /// @notice Sells fractional tokens to the pair. /// @param inputAmount The amount of fractional tokens to sell. /// @param deadline The deadline before the trade expires. /// @param minOutputAmount The minimum amount of base tokens to receive. /// @return outputAmount The amount of base tokens received. function sell(uint256 inputAmount, uint256 minOutputAmount, uint256 deadline) public returns (uint256 outputAmount) { // *** Checks *** // // check that the trade has not expired require(deadline == 0 || deadline >= block.timestamp, "Expired"); // calculate output amount using xyk invariant outputAmount = sellQuote(inputAmount); // check that the outputted amount of fractional tokens is greater than the min amount require(outputAmount >= minOutputAmount, "Slippage: amount out"); // *** Effects *** // // transfer fractional tokens from sender _transferFrom(msg.sender, address(this), inputAmount); // *** Interactions *** // if (baseToken == address(0)) { // transfer ether out msg.sender.safeTransferETH(outputAmount); } else { // transfer base tokens out ERC20(baseToken).safeTransfer(msg.sender, outputAmount); } emit Sell(inputAmount, outputAmount); } // ******************** // // Wrap logic // // ******************** // /// @notice Wraps NFTs into fractional tokens. /// @param tokenIds The ids of the NFTs to wrap. /// @param proofs The merkle proofs for the NFTs proving that they can be used in the pair. /// @return fractionalTokenAmount The amount of fractional tokens minted. function wrap(uint256[] calldata tokenIds, bytes32[][] calldata proofs, ReservoirOracle.Message[] calldata messages) public returns (uint256 fractionalTokenAmount) { // *** Checks *** // // check that wrapping is not closed require(closeTimestamp == 0, "Wrap: closed"); // check the tokens exist in the merkle root _validateTokenIds(tokenIds, proofs); // check that the tokens are not stolen with reservoir oracle _validateTokensAreNotStolen(tokenIds, messages); // *** Effects *** // // mint fractional tokens to sender fractionalTokenAmount = tokenIds.length * ONE; _mint(msg.sender, fractionalTokenAmount); // *** Interactions *** // // transfer nfts from sender for (uint256 i = 0; i < tokenIds.length;) { ERC721(nft).safeTransferFrom(msg.sender, address(this), tokenIds[i]); unchecked { i++; } } emit Wrap(tokenIds); } /// @notice Unwraps fractional tokens into NFTs. /// @param tokenIds The ids of the NFTs to unwrap. /// @param withFee Whether to pay a fee for unwrapping or not. /// @return fractionalTokenAmount The amount of fractional tokens burned. function unwrap(uint256[] calldata tokenIds, bool withFee) public returns (uint256 fractionalTokenAmount) { // *** Effects *** // // burn fractional tokens from sender fractionalTokenAmount = tokenIds.length * ONE; _burn(msg.sender, fractionalTokenAmount); // Take the fee if withFee is true if (withFee) { // calculate fee uint256 fee = fractionalTokenAmount * 3 / 1000; // transfer fee from sender _transferFrom(msg.sender, address(this), fee); fractionalTokenAmount += fee; } // transfer nfts to sender for (uint256 i = 0; i < tokenIds.length;) { ERC721(nft).safeTransferFrom(address(this), msg.sender, tokenIds[i]); unchecked { i++; } } emit Unwrap(tokenIds); } // *********************** // // NFT AMM logic // // *********************** // /// @notice nftAdd Adds liquidity to the pair using NFTs. /// @param baseTokenAmount The amount of base tokens to add. /// @param tokenIds The ids of the NFTs to add. /// @param minLpTokenAmount The minimum amount of lp tokens to receive. /// @param minPrice The minimum price of the pair. /// @param maxPrice The maximum price of the pair. /// @param deadline The deadline for the transaction. /// @param proofs The merkle proofs for the NFTs. /// @return lpTokenAmount The amount of lp tokens minted. 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) { // wrap the incoming NFTs into fractional tokens uint256 fractionalTokenAmount = wrap(tokenIds, proofs, messages); // add liquidity using the fractional tokens and base tokens lpTokenAmount = add(baseTokenAmount, fractionalTokenAmount, minLpTokenAmount, minPrice, maxPrice, deadline); } /// @notice Removes liquidity from the pair using NFTs. /// @param lpTokenAmount The amount of lp tokens to remove. /// @param minBaseTokenOutputAmount The minimum amount of base tokens to receive. /// @param deadline The deadline before the trade expires. /// @param tokenIds The ids of the NFTs to remove. /// @param withFee Whether to pay a fee for unwrapping or not. /// @return baseTokenOutputAmount The amount of base tokens received. /// @return fractionalTokenOutputAmount The amount of fractional tokens received. function nftRemove( uint256 lpTokenAmount, uint256 minBaseTokenOutputAmount, uint256 deadline, uint256[] calldata tokenIds, bool withFee ) public returns (uint256 baseTokenOutputAmount, uint256 fractionalTokenOutputAmount) { // remove liquidity and send fractional tokens and base tokens to sender (baseTokenOutputAmount, fractionalTokenOutputAmount) = remove(lpTokenAmount, minBaseTokenOutputAmount, tokenIds.length * ONE, deadline); // unwrap the fractional tokens into NFTs and send to sender unwrap(tokenIds, withFee); } /// @notice Buys NFTs from the pair using base tokens. /// @param tokenIds The ids of the NFTs to buy. /// @param maxInputAmount The maximum amount of base tokens to spend. /// @param deadline The deadline before the trade expires. /// @return inputAmount The amount of base tokens spent. function nftBuy(uint256[] calldata tokenIds, uint256 maxInputAmount, uint256 deadline) public payable returns (uint256 inputAmount) { // buy fractional tokens using base tokens inputAmount = buy(tokenIds.length * ONE, maxInputAmount, deadline); // unwrap the fractional tokens into NFTs and send to sender unwrap(tokenIds, false); } /// @notice Sells NFTs to the pair for base tokens. /// @param tokenIds The ids of the NFTs to sell. /// @param minOutputAmount The minimum amount of base tokens to receive. /// @param deadline The deadline before the trade expires. /// @param proofs The merkle proofs for the NFTs. /// @return outputAmount The amount of base tokens received. function nftSell( uint256[] calldata tokenIds, uint256 minOutputAmount, uint256 deadline, bytes32[][] calldata proofs, ReservoirOracle.Message[] calldata messages ) public returns (uint256 outputAmount) { // wrap the incoming NFTs into fractional tokens uint256 inputAmount = wrap(tokenIds, proofs, messages); // sell fractional tokens for base tokens outputAmount = sell(inputAmount, minOutputAmount, deadline); } // ****************************** // // Emergency exit logic // // ****************************** // /// @notice Closes the pair to new wraps. /// @dev Can only be called by the caviar owner. This is used as an emergency exit in case /// the caviar owner suspects that the pair has been compromised. function close() public { // check that the sender is the caviar owner require(caviar.owner() == msg.sender, "Close: not owner"); // set the close timestamp with a grace period closeTimestamp = block.timestamp + CLOSE_GRACE_PERIOD; // remove the pair from the Caviar contract caviar.destroy(nft, baseToken, merkleRoot); emit Close(closeTimestamp); } /// @notice Withdraws a particular NFT from the pair. /// @dev Can only be called by the caviar owner after the close grace period has passed. This /// is used to auction off the NFTs in the pair in case NFTs get stuck due to liquidity /// imbalances. Proceeds from the auction should be distributed pro rata to fractional /// token holders. See documentation for more details. function withdraw(uint256 tokenId) public { // check that the sender is the caviar owner require(caviar.owner() == msg.sender, "Withdraw: not owner"); // check that the close period has been set require(closeTimestamp != 0, "Withdraw not initiated"); // check that the close grace period has passed require(block.timestamp >= closeTimestamp, "Not withdrawable yet"); // transfer the nft to the caviar owner ERC721(nft).safeTransferFrom(address(this), msg.sender, tokenId); emit Withdraw(tokenId); } // ***************** // // Getters // // ***************** // function baseTokenReserves() public view returns (uint256) { return _baseTokenReserves(); } function fractionalTokenReserves() public view returns (uint256) { return balanceOf[address(this)]; } /// @notice The current price of one fractional token in base tokens with 18 decimals of precision. /// @dev Calculated by dividing the base token reserves by the fractional token reserves. /// @return price The price of one fractional token in base tokens * 1e18. function price() public view returns (uint256) { uint256 exponent = baseToken == address(0) ? 18 : (36 - ERC20(baseToken).decimals()); return (_baseTokenReserves() * 10 ** exponent) / fractionalTokenReserves(); } /// @notice The amount of base tokens required to buy a given amount of fractional tokens. /// @dev Calculated using the xyk invariant and a 30bps fee. /// @param outputAmount The amount of fractional tokens to buy. /// @return inputAmount The amount of base tokens required. function buyQuote(uint256 outputAmount) public view returns (uint256) { return FixedPointMathLib.mulDivUp( outputAmount * 1000, baseTokenReserves(), (fractionalTokenReserves() - outputAmount) * 990 ); } /// @notice The amount of base tokens received for selling a given amount of fractional tokens. /// @dev Calculated using the xyk invariant and a 30bps fee. /// @param inputAmount The amount of fractional tokens to sell. /// @return outputAmount The amount of base tokens received. function sellQuote(uint256 inputAmount) public view returns (uint256) { uint256 inputAmountWithFee = inputAmount * 990; return (inputAmountWithFee * baseTokenReserves()) / ((fractionalTokenReserves() * 1000) + inputAmountWithFee); } /// @notice The amount of lp tokens received for adding a given amount of base tokens and fractional tokens. /// @dev Calculated as a share of existing deposits. If there are no existing deposits, then initializes to /// sqrt(baseTokenAmount * fractionalTokenAmount). /// @param baseTokenAmount The amount of base tokens to add. /// @param fractionalTokenAmount The amount of fractional tokens to add. /// @return lpTokenAmount The amount of lp tokens received. function addQuote(uint256 baseTokenAmount, uint256 fractionalTokenAmount, uint256 lpTokenSupply) public view returns (uint256) { if (lpTokenSupply != 0) { // calculate amount of lp tokens as a fraction of existing reserves uint256 baseTokenShare = (baseTokenAmount * lpTokenSupply) / baseTokenReserves(); uint256 fractionalTokenShare = (fractionalTokenAmount * lpTokenSupply) / fractionalTokenReserves(); return Math.min(baseTokenShare, fractionalTokenShare); } else { // if there is no liquidity then init return Math.sqrt(baseTokenAmount * fractionalTokenAmount) - MINIMUM_LIQUIDITY; } } /// @notice The amount of base tokens and fractional tokens received for burning a given amount of lp tokens. /// @dev Calculated as a share of existing deposits. /// @param lpTokenAmount The amount of lp tokens to burn. /// @return baseTokenAmount The amount of base tokens received. /// @return fractionalTokenAmount The amount of fractional tokens received. 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); } // ************************ // // Internal utils // // ************************ // function _transferFrom(address from, address to, uint256 amount) internal returns (bool) { balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. 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 filter address is not set then no need to check if nfts are stolen if (stolenNftFilterAddress == address(0)) return; // validate that nfts are not stolen StolenNftFilterOracle(stolenNftFilterAddress).validateTokensAreNotStolen(nft, tokenIds, messages); } /// @dev Validates that the given tokenIds are valid for the contract's merkle root. Reverts /// if any of the tokenId proofs are invalid. function _validateTokenIds(uint256[] calldata tokenIds, bytes32[][] calldata proofs) internal view { // if merkle root is not set then all tokens are valid if (merkleRoot == bytes32(0)) return; // validate merkle proofs against merkle root for (uint256 i = 0; i < tokenIds.length;) { bool isValid = MerkleProofLib.verify( proofs[i], merkleRoot, // double hash to prevent second preimage attacks keccak256(bytes.concat(keccak256(abi.encode(tokenIds[i])))) ); require(isValid, "Invalid merkle proof"); unchecked { i++; } } } /// @dev Returns the current base token reserves. If the base token is ETH then it ignores /// the msg.value that is being sent in the current call context - this is to ensure the /// xyk math is correct in the buy() and add() functions. function _baseTokenReserves() internal view returns (uint256) { return baseToken == address(0) ? address(this).balance - msg.value // subtract the msg.value if the base token is ETH : ERC20(baseToken).balanceOf(address(this)); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @author: manifold.xyz import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /** * @dev Royalty registry interface */ interface IRoyaltyRegistry is IERC165 { event RoyaltyOverride(address owner, address tokenAddress, address royaltyAddress); /** * Override the location of where to look up royalty information for a given token contract. * Allows for backwards compatibility and implementation of royalty logic for contracts that did not previously support them. * * @param tokenAddress - The token address you wish to override * @param royaltyAddress - The royalty override address */ function setRoyaltyLookupAddress(address tokenAddress, address royaltyAddress) external returns (bool); /** * Returns royalty address location. Returns the tokenAddress by default, or the override if it exists * * @param tokenAddress - The token address you are looking up the royalty for */ function getRoyaltyLookupAddress(address tokenAddress) external view returns (address); /** * Returns the token address that an overrideAddress is set for. * Note: will not be accurate if the override was created before this function was added. * * @param overrideAddress - The override address you are looking up the token for */ function getOverrideLookupTokenAddress(address overrideAddress) external view returns (address); /** * Whether or not the message sender can override the royalty address for the given token address * * @param tokenAddress - The token address you are looking up the royalty for */ function overrideAllowed(address tokenAddress) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; /* * ____ * /\| ~~\ * /' | ,-. `\ * | | X | | * _|________`-' |X * /' ~~~~~~~~~, * /' ,_____,/_ * ,/' ___,'~~ ; * ~~~~~~~~|~~~~~~~|--- / X,~~~~~~~~~~~~, * | | | XX'____________' * | | /' XXX| ; * | | --x| XXX,~~~~~~~~~~~~, * | | X| '____________' * | o |---~~~~\__XX\ |XX * | | XXX`\ /XXXX * ~~~~~~~~'~~~~~~~' `\xXXXXx/' \XXX * /XXXXXX\ * /XXXXXXXXXX\ * /XXXXXX/^\XXXXX\ * ~~~~~~~~ ~~~~~~~ */ import {ERC20} from "solmate/tokens/ERC20.sol"; import {ERC721, ERC721TokenReceiver} from "solmate/tokens/ERC721.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {MerkleProofLib} from "solady/utils/MerkleProofLib.sol"; import {SafeCast} from "openzeppelin/utils/math/SafeCast.sol"; import {IERC2981} from "openzeppelin/interfaces/IERC2981.sol"; import {IRoyaltyRegistry} from "royalty-registry-solidity/IRoyaltyRegistry.sol"; import {IERC3156FlashBorrower} from "openzeppelin/interfaces/IERC3156FlashLender.sol"; import {IStolenNftOracle} from "./interfaces/IStolenNftOracle.sol"; import {Factory} from "./Factory.sol"; /// @title Private Pool /// @author out.eth (@outdoteth) /// @notice A private pool is a an NFT AMM controlled by a single owner with concentrated liquidity, custom fee rates, /// stolen NFT filtering, custom NFT weightings, royalty support, and flash loans. You can create a pool and change /// these parameters to your liking. Deposit NFTs and base tokens (or ETH) into the pool to enable trading. Earn fees on /// each trade. contract PrivatePool is ERC721TokenReceiver { using SafeTransferLib for address payable; using SafeTransferLib for address; using SafeTransferLib for ERC20; /// @notice Merkle proof input for a sparse merkle multi proof. It can be generated with a library like: /// https://github.com/OpenZeppelin/merkle-tree#treegetmultiproof struct MerkleMultiProof { bytes32[] proof; bool[] flags; } // forgefmt: disable-start event Initialize(address indexed baseToken, address indexed nft, uint128 virtualBaseTokenReserves, uint128 virtualNftReserves, uint56 changeFee, uint16 feeRate, bytes32 merkleRoot, bool useStolenNftOracle, bool payRoyalties); event Buy(uint256[] tokenIds, uint256[] tokenWeights, uint256 inputAmount, uint256 feeAmount, uint256 protocolFeeAmount, uint256 royaltyFeeAmount); event Sell(uint256[] tokenIds, uint256[] tokenWeights, uint256 outputAmount, uint256 feeAmount, uint256 protocolFeeAmount, uint256 royaltyFeeAmount); event Deposit(uint256[] tokenIds, uint256 baseTokenAmount); event Withdraw(address indexed nft, uint256[] tokenIds, address token, uint256 amount); event Change(uint256[] inputTokenIds, uint256[] inputTokenWeights, uint256[] outputTokenIds, uint256[] outputTokenWeights, uint256 feeAmount, uint256 protocolFeeAmount); event SetVirtualReserves(uint128 virtualBaseTokenReserves, uint128 virtualNftReserves); event SetMerkleRoot(bytes32 merkleRoot); event SetFeeRate(uint16 feeRate); event SetUseStolenNftOracle(bool useStolenNftOracle); event SetPayRoyalties(bool payRoyalties); event SetChangeFee(uint56 changeFee); // forgefmt: disable-end error AlreadyInitialized(); error Unauthorized(); error InvalidEthAmount(); error InvalidMerkleProof(); error InsufficientInputWeight(); error FeeRateTooHigh(); error NotAvailableForFlashLoan(); error FlashLoanFailed(); error InvalidRoyaltyFee(); error InvalidTarget(); error PrivatePoolNftNotSupported(); error InvalidTokenWeights(); error VirtualReservesNotSet(); /// @notice The address of the base ERC20 token. address public baseToken; /// @notice The address of the nft. address public nft; /// @notice The change/flash fee to 4 decimals of precision. For example, 0.0025 ETH = 25. 500 USDC = 5_000_000. uint56 public changeFee; /// @notice The buy/sell fee rate (in basis points) 200 = 2% uint16 public feeRate; /// @notice Whether or not the pool has been initialized. bool public initialized; /// @notice Whether or not the pool pays royalties to the NFT creator on each trade. bool public payRoyalties; /// @notice Whether or not the pool uses the stolen NFT oracle to check if an NFT is stolen. bool public useStolenNftOracle; /// @notice The virtual base token reserves used in the xy=k invariant. Changing this will change the liquidity /// depth and price of the pool. uint128 public virtualBaseTokenReserves; /// @notice The virtual nft reserves used in the xy=k invariant. Changing this will change the liquidity /// depth and price of the pool. /// @dev The virtual NFT reserves that a user sets. If it's desired to set the reserves to match 16 NFTs then the /// virtual reserves should be set to 16e18. If weights are enabled by setting the merkle root to be non-zero then /// the virtual reserves should be set to the sum of the weights of the NFTs; where floor NFTs all have a weight of /// 1e18. A rarer NFT may have a weight of 2.3e18 if it's 2.3x more valuable than a floor. uint128 public virtualNftReserves; /// @notice The merkle root of all the token weights in the pool. If the merkle root is set to bytes32(0) then all /// NFTs are set to have a weight of 1e18. bytes32 public merkleRoot; /// @notice The NFT oracle to check if an NFT is stolen. address public immutable stolenNftOracle; /// @notice The factory contract that created this pool. address payable public immutable factory; /// @notice The royalty registry from manifold.xyz. address public immutable royaltyRegistry; modifier onlyOwner() virtual { if (msg.sender != Factory(factory).ownerOf(uint160(address(this)))) { revert Unauthorized(); } _; } receive() external payable {} /// @dev This is only called when the base implementation contract is deployed. The following immutable parameters /// are set: /// - factory: The address of the factory contract /// - royaltyRegistry: The address of the royalty registry from manifold.xyz /// - stolenNftOracle: The address of the stolen NFT oracle /// These are all stored in immutable storage, which enables all minimal proxy contracts to read them without /// incurring additional deployment costs and re-initializing them at point of creation in the factory contract. constructor(address _factory, address _royaltyRegistry, address _stolenNftOracle) { factory = payable(_factory); royaltyRegistry = _royaltyRegistry; stolenNftOracle = _stolenNftOracle; } /// @notice Initializes the private pool and sets the initial parameters. Should only be called once by the factory. /// @param _baseToken The address of the base token /// @param _nft The address of the NFT /// @param _virtualBaseTokenReserves The virtual base token reserves /// @param _virtualNftReserves The virtual NFT reserves /// @param _feeRate The fee rate (in basis points) 200 = 2% /// @param _merkleRoot The merkle root /// @param _useStolenNftOracle Whether or not the pool uses the stolen NFT oracle to check if an NFT is stolen function initialize( address _baseToken, address _nft, uint128 _virtualBaseTokenReserves, uint128 _virtualNftReserves, uint56 _changeFee, uint16 _feeRate, bytes32 _merkleRoot, bool _useStolenNftOracle, bool _payRoyalties ) public { // prevent duplicate initialization if (initialized) revert AlreadyInitialized(); // check that the fee rate is less than 50% if (_feeRate > 5_000) revert FeeRateTooHigh(); // check that the nft is not a private pool NFT if (_nft == factory) revert PrivatePoolNftNotSupported(); // set the state variables baseToken = _baseToken; nft = _nft; virtualBaseTokenReserves = _virtualBaseTokenReserves; virtualNftReserves = _virtualNftReserves; changeFee = _changeFee; feeRate = _feeRate; merkleRoot = _merkleRoot; useStolenNftOracle = _useStolenNftOracle; payRoyalties = _payRoyalties; // mark the pool as initialized initialized = true; // emit the event emit Initialize( _baseToken, _nft, _virtualBaseTokenReserves, _virtualNftReserves, _changeFee, _feeRate, _merkleRoot, _useStolenNftOracle, _payRoyalties ); } /// @notice Buys NFTs from the pool, paying with base tokens from the caller. Then transfers the bought NFTs to the /// caller. The net cost depends on the current price, fee rate and assigned NFT weights. /// @dev DO NOT call this function directly unless you know what you are doing. Instead, use a wrapper contract that /// will check the max input amount and revert if the slippage is too high. /// @param tokenIds The token IDs of the NFTs to buy. /// @param tokenWeights The weights of the NFTs to buy. /// @param proof The merkle proof for the weights of each NFT to buy. /// @return netInputAmount The amount of base tokens spent inclusive of fees. /// @return feeAmount The amount of base tokens spent on fees. /// @return protocolFeeAmount The amount of base tokens spent on protocol fees. function buy(uint256[] calldata tokenIds, uint256[] calldata tokenWeights, MerkleMultiProof calldata proof) public payable returns (uint256 netInputAmount, uint256 feeAmount, uint256 protocolFeeAmount) { // ~~~ Checks ~~~ // // check that virtual reserves are set if (virtualBaseTokenReserves == 0 || virtualNftReserves == 0) revert VirtualReservesNotSet(); // calculate the sum of weights of the NFTs to buy uint256 weightSum = sumWeightsAndValidateProof(tokenIds, tokenWeights, proof); // calculate the required net input amount and fee amount (netInputAmount, feeAmount, protocolFeeAmount) = buyQuote(weightSum); // check that the caller sent 0 ETH if the base token is not ETH if (baseToken != address(0) && msg.value > 0) revert InvalidEthAmount(); // ~~~ Effects ~~~ // // update the virtual reserves virtualBaseTokenReserves += SafeCast.toUint128(netInputAmount - feeAmount - protocolFeeAmount); virtualNftReserves -= SafeCast.toUint128(weightSum); // ~~~ Interactions ~~~ // if (baseToken != address(0)) { // transfer the base token from the caller to the contract ERC20(baseToken).safeTransferFrom(msg.sender, address(this), netInputAmount); // if the protocol fee is set then pay the protocol fee if (protocolFeeAmount > 0) ERC20(baseToken).safeTransfer(factory, protocolFeeAmount); } // calculate the sale price (assume it's the same for each NFT even if weights differ) uint256 salePrice = (netInputAmount - feeAmount - protocolFeeAmount) / tokenIds.length; uint256 royaltyFeeAmount = 0; for (uint256 i = 0; i < tokenIds.length; i++) { // transfer the NFT to the caller ERC721(nft).safeTransferFrom(address(this), msg.sender, tokenIds[i]); if (payRoyalties) { // get the royalty fee for the NFT (uint256 royaltyFee, address recipient) = _getRoyalty(tokenIds[i], salePrice); if (royaltyFee > 0 && recipient != address(0)) { // add the royalty fee amount to the net input amount netInputAmount += royaltyFee; // transfer the royalties to the recipient if (baseToken != address(0)) { ERC20(baseToken).safeTransferFrom(msg.sender, recipient, royaltyFee); } else { recipient.safeTransferETH(royaltyFee); } } } } if (baseToken == address(0)) { // check that the caller sent enough ETH to cover the net required input if (msg.value < netInputAmount) revert InvalidEthAmount(); // if the protocol fee is set then pay the protocol fee if (protocolFeeAmount > 0) factory.safeTransferETH(protocolFeeAmount); // refund any excess ETH to the caller if (msg.value > netInputAmount) msg.sender.safeTransferETH(msg.value - netInputAmount); } // emit the buy event emit Buy(tokenIds, tokenWeights, netInputAmount, feeAmount, protocolFeeAmount, royaltyFeeAmount); } /// @notice Sells NFTs into the pool and transfers base tokens to the caller. NFTs are transferred from the caller /// to the pool. The net sale amount depends on the current price, fee rate and assigned NFT weights. /// @dev DO NOT call this function directly unless you know what you are doing. Instead, use a wrapper contract that /// will check the min output amount and revert if the slippage is too high. /// @param tokenIds The token IDs of the NFTs to sell. /// @param tokenWeights The weights of the NFTs to sell. /// @param proof The merkle proof for the weights of each NFT to sell. /// @param stolenNftProofs The proofs that show each NFT is not stolen. /// @return netOutputAmount The amount of base tokens received inclusive of fees. /// @return feeAmount The amount of base tokens to pay in fees. /// @return protocolFeeAmount The amount of base tokens to pay in protocol fees. function sell( uint256[] calldata tokenIds, uint256[] calldata tokenWeights, MerkleMultiProof calldata proof, IStolenNftOracle.Message[] memory stolenNftProofs // put in memory to avoid stack too deep error ) public returns (uint256 netOutputAmount, uint256 feeAmount, uint256 protocolFeeAmount) { // ~~~ Checks ~~~ // // check that virtual reserves are set if (virtualBaseTokenReserves == 0 || virtualNftReserves == 0) revert VirtualReservesNotSet(); // calculate the sum of weights of the NFTs to sell uint256 weightSum = sumWeightsAndValidateProof(tokenIds, tokenWeights, proof); // calculate the net output amount and fee amount (netOutputAmount, feeAmount, protocolFeeAmount) = sellQuote(weightSum); // check the nfts are not stolen if (useStolenNftOracle) { IStolenNftOracle(stolenNftOracle).validateTokensAreNotStolen(nft, tokenIds, stolenNftProofs); } // ~~~ Effects ~~~ // // update the virtual reserves virtualBaseTokenReserves -= SafeCast.toUint128(netOutputAmount + protocolFeeAmount + feeAmount); virtualNftReserves += SafeCast.toUint128(weightSum); // ~~~ Interactions ~~~ // uint256 royaltyFeeAmount = 0; for (uint256 i = 0; i < tokenIds.length; i++) { // transfer each nft from the caller ERC721(nft).safeTransferFrom(msg.sender, address(this), tokenIds[i]); if (payRoyalties) { // calculate the sale price (assume it's the same for each NFT even if weights differ) uint256 salePrice = (netOutputAmount + feeAmount + protocolFeeAmount) / tokenIds.length; // get the royalty fee for the NFT (uint256 royaltyFee, address recipient) = _getRoyalty(tokenIds[i], salePrice); // transfer the royalty fee to the recipient if it's greater than 0 if (royaltyFee > 0 && recipient != address(0)) { // tally the royalty fee amount royaltyFeeAmount += royaltyFee; if (baseToken != address(0)) { ERC20(baseToken).safeTransfer(recipient, royaltyFee); } else { recipient.safeTransferETH(royaltyFee); } } } } // subtract the royalty fee amount from the net output amount netOutputAmount -= royaltyFeeAmount; if (baseToken == address(0)) { // transfer ETH to the caller msg.sender.safeTransferETH(netOutputAmount); // if the protocol fee is set then pay the protocol fee if (protocolFeeAmount > 0) factory.safeTransferETH(protocolFeeAmount); } else { // transfer base tokens to the caller ERC20(baseToken).transfer(msg.sender, netOutputAmount); // if the protocol fee is set then pay the protocol fee if (protocolFeeAmount > 0) ERC20(baseToken).safeTransfer(factory, protocolFeeAmount); } // emit the sell event emit Sell(tokenIds, tokenWeights, netOutputAmount, feeAmount, protocolFeeAmount, royaltyFeeAmount); } /// @notice Changes a set of NFTs that the caller owns for another set of NFTs in the pool. The caller must approve /// the pool to transfer the NFTs. The sum of the caller's NFT weights must be greater than or equal to the sum of /// the output pool NFTs weights. The caller must also pay a fee depending the net input weight and change fee /// amount. /// @param inputTokenIds The token IDs of the NFTs to change. /// @param inputTokenWeights The weights of the NFTs to change. /// @param inputProof The merkle proof for the weights of each NFT to change. /// @param stolenNftProofs The proofs that show each input NFT is not stolen. /// @param outputTokenIds The token IDs of the NFTs to receive. /// @param outputTokenWeights The weights of the NFTs to receive. /// @param outputProof The merkle proof for the weights of each NFT to receive. /// @return feeAmount The amount of base tokens to pay in fees. /// @return protocolFeeAmount The amount of base tokens to pay in protocol fees. function change( uint256[] memory inputTokenIds, uint256[] memory inputTokenWeights, MerkleMultiProof memory inputProof, IStolenNftOracle.Message[] memory stolenNftProofs, uint256[] memory outputTokenIds, uint256[] memory outputTokenWeights, MerkleMultiProof memory outputProof ) public payable returns (uint256 feeAmount, uint256 protocolFeeAmount) { // ~~~ Checks ~~~ // // check that the caller sent 0 ETH if base token is not ETH if (baseToken != address(0) && msg.value > 0) revert InvalidEthAmount(); // check that NFTs are not stolen if (useStolenNftOracle) { IStolenNftOracle(stolenNftOracle).validateTokensAreNotStolen(nft, inputTokenIds, stolenNftProofs); } // fix stack too deep { // calculate the sum of weights for the input nfts uint256 inputWeightSum = sumWeightsAndValidateProof(inputTokenIds, inputTokenWeights, inputProof); // calculate the sum of weights for the output nfts uint256 outputWeightSum = sumWeightsAndValidateProof(outputTokenIds, outputTokenWeights, outputProof); // check that the input weights are greater than or equal to the output weights if (inputWeightSum < outputWeightSum) revert InsufficientInputWeight(); // calculate the fee amount (feeAmount, protocolFeeAmount) = changeFeeQuote(inputWeightSum); } // ~~~ Interactions ~~~ // if (baseToken != address(0)) { // transfer the fee amount of base tokens from the caller ERC20(baseToken).safeTransferFrom(msg.sender, address(this), feeAmount); // if the protocol fee is non-zero then transfer the protocol fee to the factory if (protocolFeeAmount > 0) ERC20(baseToken).safeTransferFrom(msg.sender, factory, protocolFeeAmount); } else { // check that the caller sent enough ETH to cover the fee amount and protocol fee if (msg.value < feeAmount + protocolFeeAmount) revert InvalidEthAmount(); // if the protocol fee is non-zero then transfer the protocol fee to the factory if (protocolFeeAmount > 0) factory.safeTransferETH(protocolFeeAmount); // refund any excess ETH to the caller if (msg.value > feeAmount + protocolFeeAmount) { msg.sender.safeTransferETH(msg.value - feeAmount - protocolFeeAmount); } } // transfer the input nfts from the caller for (uint256 i = 0; i < inputTokenIds.length; i++) { ERC721(nft).safeTransferFrom(msg.sender, address(this), inputTokenIds[i]); } // transfer the output nfts to the caller for (uint256 i = 0; i < outputTokenIds.length; i++) { ERC721(nft).safeTransferFrom(address(this), msg.sender, outputTokenIds[i]); } // emit the change event emit Change(inputTokenIds, inputTokenWeights, outputTokenIds, outputTokenWeights, feeAmount, protocolFeeAmount); } /// @notice Executes a transaction from the pool account to a target contract. The caller must be the owner of the /// pool. This allows for use cases such as claiming airdrops. /// @param target The address of the target contract. /// @param data The data to send to the target contract. /// @return returnData The return data of the transaction. function execute(address target, bytes memory data) public payable onlyOwner returns (bytes memory) { if (target == address(baseToken) || target == address(nft)) revert InvalidTarget(); // call the target with the value and data (bool success, bytes memory returnData) = target.call{value: msg.value}(data); // if the call succeeded return the return data if (success) return returnData; // if we got an error bubble up the error message if (returnData.length > 0) { // solhint-disable-next-line no-inline-assembly assembly { let returnData_size := mload(returnData) revert(add(32, returnData), returnData_size) } } revert(); } /// @notice Deposits base tokens and NFTs into the pool. The caller must approve the pool to transfer their NFTs and /// base tokens. /// @dev DO NOT call this function directly unless you know what you are doing. Instead, use a wrapper contract that /// will check the current price is within the desired bounds. /// @param tokenIds The token IDs of the NFTs to deposit. /// @param baseTokenAmount The amount of base tokens to deposit. function deposit(uint256[] calldata tokenIds, uint256 baseTokenAmount) public payable { // ~~~ Checks ~~~ // // ensure the caller sent a valid amount of ETH if base token is ETH or that the caller sent 0 ETH if base token // is not ETH if ((baseToken == address(0) && msg.value != baseTokenAmount) || (msg.value > 0 && baseToken != address(0))) { revert InvalidEthAmount(); } // ~~~ Interactions ~~~ // // transfer the nfts from the caller for (uint256 i = 0; i < tokenIds.length; i++) { ERC721(nft).safeTransferFrom(msg.sender, address(this), tokenIds[i]); } if (baseToken != address(0)) { // transfer the base tokens from the caller ERC20(baseToken).safeTransferFrom(msg.sender, address(this), baseTokenAmount); } // emit the deposit event emit Deposit(tokenIds, baseTokenAmount); } /// @notice Withdraws NFTs and tokens from the pool. Can only be called by the owner of the pool. /// @param _nft The address of the NFT. /// @param tokenIds The token IDs of the NFTs to withdraw. /// @param token The address of the token to withdraw. /// @param tokenAmount The amount of tokens to withdraw. function withdraw(address _nft, uint256[] calldata tokenIds, address token, uint256 tokenAmount) public onlyOwner { // ~~~ Interactions ~~~ // // transfer the nfts to the caller for (uint256 i = 0; i < tokenIds.length; i++) { ERC721(_nft).safeTransferFrom(address(this), msg.sender, tokenIds[i]); } if (token == address(0)) { // transfer the ETH to the caller msg.sender.safeTransferETH(tokenAmount); } else { // transfer the tokens to the caller ERC20(token).transfer(msg.sender, tokenAmount); } // emit the withdraw event emit Withdraw(_nft, tokenIds, token, tokenAmount); } /// @notice Sets the virtual base token reserves and virtual NFT reserves. Can only be called by the owner of the /// pool. These parameters affect the price and liquidity depth of the pool. /// @param newVirtualBaseTokenReserves The new virtual base token reserves. /// @param newVirtualNftReserves The new virtual NFT reserves. function setVirtualReserves(uint128 newVirtualBaseTokenReserves, uint128 newVirtualNftReserves) public onlyOwner { // set the virtual base token reserves and virtual nft reserves virtualBaseTokenReserves = newVirtualBaseTokenReserves; virtualNftReserves = newVirtualNftReserves; // emit the set virtual reserves event emit SetVirtualReserves(newVirtualBaseTokenReserves, newVirtualNftReserves); } /// @notice Sets the merkle root. Can only be called by the owner of the pool. The merkle root is used to validate /// the NFT weights. /// @param newMerkleRoot The new merkle root. function setMerkleRoot(bytes32 newMerkleRoot) public onlyOwner { // set the merkle root merkleRoot = newMerkleRoot; // emit the set merkle root event emit SetMerkleRoot(newMerkleRoot); } /// @notice Sets the fee rate. Can only be called by the owner of the pool. The fee rate is used to calculate the /// fee amount when swapping NFTs. The fee rate is in basis points (1/100th of a percent). For example, /// 10_000 == 100%, 200 == 2%, 1 == 0.01%. /// @param newFeeRate The new fee rate (in basis points) function setFeeRate(uint16 newFeeRate) public onlyOwner { // check that the fee rate is less than or equal to 50% if (newFeeRate > 5_000) revert FeeRateTooHigh(); // set the fee rate feeRate = newFeeRate; // emit the set fee rate event emit SetFeeRate(newFeeRate); } /// @notice Sets the whether or not to use the stolen NFT oracle. Can only be called by the owner of the pool. The /// stolen NFT oracle is used to check if an NFT is stolen. /// @param newUseStolenNftOracle The new use stolen NFT oracle flag. function setUseStolenNftOracle(bool newUseStolenNftOracle) public onlyOwner { // set the use stolen NFT oracle flag useStolenNftOracle = newUseStolenNftOracle; // emit the set use stolen NFT oracle event emit SetUseStolenNftOracle(newUseStolenNftOracle); } /// @notice Sets the pay royalties flag. Can only be called by the owner of the pool. If royalties are enabled then /// the pool will pay royalties when buying or selling NFTs. /// @param newPayRoyalties The new pay royalties flag. function setPayRoyalties(bool newPayRoyalties) public onlyOwner { // set the pay royalties flag payRoyalties = newPayRoyalties; // emit the set pay royalties event emit SetPayRoyalties(newPayRoyalties); } /// @notice Sets the change fee. Can only be called by the owner. The change fee is used to calculate the /// fixed fee amount when changing or flashloaning NFTs. The fee rate is to 4 decimals of accuracy. For /// example, 0.0025 ETH = 25. 500 USDC = 5_000_000. /// @param _newChangeFee The new pay change fee. function setChangeFee(uint56 _newChangeFee) public onlyOwner { // set the pay royalties flag changeFee = _newChangeFee; // emit the set pay royalties event emit SetChangeFee(changeFee); } /// @notice Updates all parameter settings in one go. /// @param newVirtualBaseTokenReserves The new virtual base token reserves. /// @param newVirtualNftReserves The new virtual NFT reserves. /// @param newMerkleRoot The new merkle root. /// @param newFeeRate The new fee rate (in basis points) /// @param newUseStolenNftOracle The new use stolen NFT oracle flag. /// @param newPayRoyalties The new pay royalties flag. /// @param newChangeFee The new change fee. function setAllParameters( uint128 newVirtualBaseTokenReserves, uint128 newVirtualNftReserves, bytes32 newMerkleRoot, uint16 newFeeRate, bool newUseStolenNftOracle, bool newPayRoyalties, uint56 newChangeFee ) public { setVirtualReserves(newVirtualBaseTokenReserves, newVirtualNftReserves); setMerkleRoot(newMerkleRoot); setFeeRate(newFeeRate); setUseStolenNftOracle(newUseStolenNftOracle); setPayRoyalties(newPayRoyalties); setChangeFee(newChangeFee); } /// @notice Executes a flash loan. /// @param receiver The receiver of the flash loan. /// @param token The address of the NFT contract. /// @param tokenId The ID of the NFT. /// @param data The data to pass to the receiver. /// @return success Whether or not the flash loan was successful. function flashLoan(IERC3156FlashBorrower receiver, address token, uint256 tokenId, bytes calldata data) external payable returns (bool) { // check that the NFT is available for a flash loan if (!availableForFlashLoan(token, tokenId)) revert NotAvailableForFlashLoan(); // calculate the fee (uint256 flashFee, uint256 protocolFee) = flashFeeAndProtocolFee(); uint256 fee = flashFee + protocolFee; // if base token is ETH then check that caller sent enough for the fee or if base token is not ETH // then check that the user sent 0 ETH if ((baseToken == address(0) && msg.value < fee) || (baseToken != address(0) && msg.value > 0)) { revert InvalidEthAmount(); } // transfer the NFT to the borrower ERC721(token).safeTransferFrom(address(this), address(receiver), tokenId); // call the borrower bool success = receiver.onFlashLoan(msg.sender, token, tokenId, fee, data) == keccak256("ERC3156FlashBorrower.onFlashLoan"); // check that flashloan was successful if (!success) revert FlashLoanFailed(); // transfer the NFT from the borrower ERC721(token).safeTransferFrom(address(receiver), address(this), tokenId); if (baseToken != address(0)) { // transfer the fee from the borrower ERC20(baseToken).safeTransferFrom(msg.sender, address(this), flashFee); // transfer the protocol fee to the factory ERC20(baseToken).safeTransferFrom(msg.sender, factory, protocolFee); } else { // transfer the protocol fee to the factory factory.safeTransferETH(protocolFee); // refund the excess ETH to the borrower if (msg.value > fee) msg.sender.safeTransferETH(msg.value - fee); } return success; } /// @notice Sums the weights of each NFT and validates that the weights are correct by verifying the merkle proof. /// @param tokenIds The token IDs of the NFTs to sum the weights for. /// @param tokenWeights The weights of each NFT in the token IDs array. /// @param proof The merkle proof for the weights of each NFT. /// @return sum The sum of the weights of each NFT. function sumWeightsAndValidateProof( uint256[] memory tokenIds, uint256[] memory tokenWeights, MerkleMultiProof memory proof ) public view returns (uint256) { if (merkleRoot == bytes32(0)) { // if the merkle root is not set then check that the token weights array is empty if (tokenWeights.length > 0) revert InvalidTokenWeights(); // if the merkle root is not set then set the weight of each nft to be 1e18 return tokenIds.length * 1e18; } uint256 sum; bytes32[] memory leafs = new bytes32[](tokenIds.length); for (uint256 i = 0; i < tokenIds.length; i++) { // create the leaf for the merkle proof leafs[i] = keccak256(bytes.concat(keccak256(abi.encode(tokenIds[i], tokenWeights[i])))); // sum each token weight sum += tokenWeights[i]; } // validate that the weights are valid against the merkle proof if (!MerkleProofLib.verifyMultiProof(proof.proof, merkleRoot, leafs, proof.flags)) { revert InvalidMerkleProof(); } return sum; } /// @notice Returns the required input of buying a given amount of NFTs inclusive of the fee which is dependent on /// the currently set fee rate. /// @param outputAmount The amount of NFTs to buy multiplied by 1e18. /// @return netInputAmount The required input amount of base tokens inclusive of the fee. /// @return feeAmount The fee amount. /// @return protocolFeeAmount The protocol fee amount. function buyQuote(uint256 outputAmount) public view returns (uint256 netInputAmount, uint256 feeAmount, uint256 protocolFeeAmount) { // calculate the input amount based on xy=k invariant and round up by 1 wei uint256 inputAmount = FixedPointMathLib.mulDivUp(outputAmount, virtualBaseTokenReserves, (virtualNftReserves - outputAmount)); protocolFeeAmount = inputAmount * Factory(factory).protocolFeeRate() / 10_000; feeAmount = inputAmount * feeRate / 10_000; netInputAmount = inputAmount + feeAmount + protocolFeeAmount; } /// @notice Returns the output amount of selling a given amount of NFTs inclusive of the fee which is dependent on /// the currently set fee rate. /// @param inputAmount The amount of NFTs to sell multiplied by 1e18. /// @return netOutputAmount The output amount of base tokens inclusive of the fee. /// @return feeAmount The fee amount. /// @return protocolFeeAmount The protocol fee amount. function sellQuote(uint256 inputAmount) public view returns (uint256 netOutputAmount, uint256 feeAmount, uint256 protocolFeeAmount) { // calculate the output amount based on xy=k invariant uint256 outputAmount = inputAmount * virtualBaseTokenReserves / (virtualNftReserves + inputAmount); protocolFeeAmount = outputAmount * Factory(factory).protocolFeeRate() / 10_000; feeAmount = outputAmount * feeRate / 10_000; netOutputAmount = outputAmount - feeAmount - protocolFeeAmount; } /// @notice Returns the fee required to change a given amount of NFTs. The fee is based on the current changeFee /// (which contains 4 decimals of precision) multiplied by some exponent depending on the base token decimals. /// @param inputAmount The amount of NFTs to change multiplied by 1e18. /// @return feeAmount The fee amount. /// @return protocolFeeAmount The protocol fee amount. function changeFeeQuote(uint256 inputAmount) public view returns (uint256 feeAmount, uint256 protocolFeeAmount) { // multiply the changeFee to get the fee per NFT (4 decimals of accuracy) uint256 exponent = baseToken == address(0) ? 18 - 4 : ERC20(baseToken).decimals() - 4; uint256 feePerNft = changeFee * 10 ** exponent; feeAmount = inputAmount * feePerNft / 1e18; protocolFeeAmount = feeAmount * Factory(factory).protocolChangeFeeRate() / 10_000; } /// @notice Returns the price of the pool to 18 decimals of accuracy. /// @return price The price of the pool. function price() public view returns (uint256) { // ensure that the exponent is always to 18 decimals of accuracy uint256 exponent = baseToken == address(0) ? 18 : (36 - ERC20(baseToken).decimals()); return (virtualBaseTokenReserves * 10 ** exponent) / virtualNftReserves; } /// @notice Returns the fee and protocol fee required to flash swap a given NFT. /// @return feeAmount The fee amount. /// @return protocolFeeAmount The protocol fee amount. function flashFeeAndProtocolFee() public view returns (uint256 feeAmount, uint256 protocolFeeAmount) { // multiply the changeFee to get the fee per NFT (4 decimals of accuracy) uint256 exponent = baseToken == address(0) ? 18 - 4 : ERC20(baseToken).decimals() - 4; feeAmount = changeFee * 10 ** exponent; protocolFeeAmount = feeAmount * Factory(factory).protocolChangeFeeRate() / 10_000; } /// @notice Returns the fee required to flash swap a given NFT. /// @return feeAmount The fee amount. function flashFee(address, uint256) public view returns (uint256) { (uint256 feeAmount, uint256 protocolFeeAmount) = flashFeeAndProtocolFee(); return feeAmount + protocolFeeAmount; } /// @notice Returns the token that is used to pay the flash fee. function flashFeeToken() public view returns (address) { return baseToken; } /// @notice Returns whether or not an NFT is available for a flash loan. /// @param token The address of the NFT contract. /// @param tokenId The ID of the NFT. /// @return available Whether or not the NFT is available for a flash loan. function availableForFlashLoan(address token, uint256 tokenId) public view returns (bool) { // return if the NFT is owned by this contract try ERC721(token).ownerOf(tokenId) returns (address result) { return result == address(this); } catch { return false; } } /// @notice Gets the royalty and recipient for a given NFT and sale price. Looks up the royalty info from the /// manifold registry. /// @param tokenId The token ID of the NFT. /// @param salePrice The sale price of the NFT. /// @return royaltyFee The royalty fee to pay. /// @return recipient The address to pay the royalty fee to. function _getRoyalty(uint256 tokenId, uint256 salePrice) internal view returns (uint256 royaltyFee, address recipient) { // get the royalty lookup address address lookupAddress = IRoyaltyRegistry(royaltyRegistry).getRoyaltyLookupAddress(nft); if (IERC2981(lookupAddress).supportsInterface(type(IERC2981).interfaceId)) { // get the royalty fee from the registry (recipient, royaltyFee) = IERC2981(lookupAddress).royaltyInfo(tokenId, salePrice); // revert if the royalty fee is greater than the sale price if (royaltyFee > salePrice) revert InvalidRoyaltyFee(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; interface IStolenNftOracle { // copied from https://github.com/reservoirprotocol/oracle/blob/main/contracts/ReservoirOracle.sol struct Message { bytes32 id; bytes payload; // The UNIX timestamp when the message was signed by the oracle uint256 timestamp; // ECDSA signature or EIP-2098 compact signature bytes signature; } /// @notice Validates that a set of token ids have not been marked as stolen by the oracle. /// @dev Check a signed message from the oracle to ensure that the token ids have not been marked as stolen. /// @param tokenAddress The address of the token contract. /// @param tokenIds The token ids to validate. /// @param proofs The proofs that the token ids have not been marked as stolen. function validateTokensAreNotStolen(address tokenAddress, uint256[] calldata tokenIds, Message[] calldata proofs) external; }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ 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; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. 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]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ 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 because the only math done is incrementing // the owner's nonce which cannot realistically overflow. 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) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; /// @notice Gas optimized merkle proof verification library. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol) /// @author Modified from Solady (https://github.com/Vectorized/solady/blob/main/src/utils/MerkleProofLib.sol) library MerkleProofLib { function verify( bytes32[] calldata proof, bytes32 root, bytes32 leaf ) internal pure returns (bool isValid) { /// @solidity memory-safe-assembly assembly { if proof.length { // Left shifting by 5 is like multiplying by 32. let end := add(proof.offset, shl(5, proof.length)) // Initialize offset to the offset of the proof in calldata. let offset := proof.offset // Iterate over proof elements to compute root hash. // prettier-ignore for {} 1 {} { // Slot where the leaf should be put in scratch space. If // leaf > calldataload(offset): slot 32, otherwise: slot 0. let leafSlot := shl(5, gt(leaf, calldataload(offset))) // Store elements to hash contiguously in scratch space. // The xor puts calldataload(offset) in whichever slot leaf // is not occupying, so 0 if leafSlot is 32, and 32 otherwise. mstore(leafSlot, leaf) mstore(xor(leafSlot, 32), calldataload(offset)) // Reuse leaf to store the hash to reduce stack operations. leaf := keccak256(0, 64) // Hash both slots of scratch space. offset := add(offset, 32) // Shift 1 word per cycle. // prettier-ignore if iszero(lt(offset, end)) { break } } } isValid := eq(leaf, root) // The proof is valid if the roots match. } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) library FixedPointMathLib { /*////////////////////////////////////////////////////////////// SIMPLIFIED FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ uint256 internal constant MAX_UINT256 = 2**256 - 1; uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. } function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. } function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. } function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. } /*////////////////////////////////////////////////////////////// LOW LEVEL FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ function mulDivDown( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // Divide x * y by the denominator. z := div(mul(x, y), denominator) } } function mulDivUp( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // If x * y modulo the denominator is strictly greater than 0, // 1 is added to round up the division of x * y by the denominator. 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) { /// @solidity memory-safe-assembly assembly { switch x case 0 { switch n case 0 { // 0 ** 0 = 1 z := scalar } default { // 0 ** n = 0 z := 0 } } default { switch mod(n, 2) case 0 { // If n is even, store scalar in z for now. z := scalar } default { // If n is odd, store x in z for now. z := x } // Shifting right by 1 is like dividing by 2. let half := shr(1, scalar) for { // Shift n right by 1 before looping to halve it. n := shr(1, n) } n { // Shift n right by 1 each iteration to halve it. n := shr(1, n) } { // Revert immediately if x ** 2 would overflow. // Equivalent to iszero(eq(div(xx, x), x)) here. if shr(128, x) { revert(0, 0) } // Store x squared. let xx := mul(x, x) // Round to the nearest number. let xxRound := add(xx, half) // Revert if xx + half overflowed. if lt(xxRound, xx) { revert(0, 0) } // Set x to scaled xxRound. x := div(xxRound, scalar) // If n is even: if mod(n, 2) { // Compute z * x. let zx := mul(z, x) // If z * x overflowed: if iszero(eq(div(zx, x), z)) { // Revert if x is non-zero. if iszero(iszero(x)) { revert(0, 0) } } // Round to the nearest number. let zxRound := add(zx, half) // Revert if zx + half overflowed. if lt(zxRound, zx) { revert(0, 0) } // Return properly scaled zxRound. z := div(zxRound, scalar) } } } } } /*////////////////////////////////////////////////////////////// GENERAL NUMBER UTILITIES //////////////////////////////////////////////////////////////*/ function sqrt(uint256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { let y := x // We start y at x, which will help us make our initial estimate. z := 181 // The "correct" value is 1, but this saves a multiplication later. // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. // We check y >= 2^(k + 8) but shift right by k bits // each branch to ensure that if x >= 256, then y >= 256. 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) } // Goal was to get z*z*y within a small factor of x. More iterations could // get y in a tighter range. Currently, we will have y in [256, 256*2^16). // We ensured y >= 256 so that the relative difference between y and y+1 is small. // That's not possible if x < 256 but we can just verify those cases exhaustively. // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256. // Correctness can be checked exhaustively for x < 256, so we assume y >= 256. // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps. // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256. // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18. // There is no overflow risk here since y < 2^136 after the first branch above. z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181. // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. 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))) // If x+1 is a perfect square, the Babylonian method cycles between // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor. // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case. // If you don't care whether the floor or ceil square root is returned, you can remove this statement. z := sub(z, lt(div(x, z), z)) } } function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Mod x by y. Note this will return // 0 instead of reverting if y is zero. z := mod(x, y) } } function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { // Divide x by y. Note this will return // 0 instead of reverting if y is zero. r := div(x, y) } } function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Add 1 to x * y if x % y > 0. Note this will // return 0 instead of reverting if y is zero. z := add(gt(mod(x, y), 0), div(x, y)) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ 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; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. 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); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ 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); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 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; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ 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); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 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; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ 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); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ 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; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; // Inspired by https://github.com/ZeframLou/trustus abstract contract ReservoirOracle { // --- Structs --- struct Message { bytes32 id; bytes payload; // The UNIX timestamp when the message was signed by the oracle uint256 timestamp; // ECDSA signature or EIP-2098 compact signature bytes signature; } // --- Errors --- error InvalidMessage(); // --- Fields --- address public RESERVOIR_ORACLE_ADDRESS; // --- Constructor --- constructor(address reservoirOracleAddress) { RESERVOIR_ORACLE_ADDRESS = reservoirOracleAddress; } // --- Public methods --- function updateReservoirOracleAddress(address newReservoirOracleAddress) public virtual; // --- Internal methods --- function _verifyMessage( bytes32 id, uint256 validFor, Message memory message ) internal view virtual returns (bool success) { // Ensure the message matches the requested id if (id != message.id) { return false; } // Ensure the message timestamp is valid if ( message.timestamp > block.timestamp || message.timestamp + validFor < block.timestamp ) { return false; } bytes32 r; bytes32 s; uint8 v; // Extract the individual signature fields from the signature bytes memory signature = message.signature; if (signature.length == 64) { // EIP-2098 compact signature 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) { // ECDSA signature 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", // EIP-712 structured-data hash keccak256( abi.encode( keccak256( "Message(bytes32 id,bytes payload,uint256 timestamp)" ), message.id, keccak256(message.payload), message.timestamp ) ) ) ), v, r, s ); // Ensure the signer matches the designated oracle address return signerAddress == RESERVOIR_ORACLE_ADDRESS; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import "solmate/auth/Owned.sol"; import "solmate/tokens/ERC20.sol"; /// @title LP token /// @author out.eth (@outdoteth) /// @notice LP token which is minted and burned by the Pair contract to represent liquidity in the pool. contract LpToken is Owned, ERC20 { constructor(string memory pairSymbol) Owned(msg.sender) ERC20(string.concat(pairSymbol, " LP token"), string.concat("LP-", pairSymbol), 18) {} /// @notice Mints new LP tokens to the given address. /// @param to The address to mint to. /// @param amount The amount to mint. function mint(address to, uint256 amount) public onlyOwner { _mint(to, amount); } /// @notice Burns LP tokens from the given address. /// @param from The address to burn from. /// @param amount The amount to burn. function burn(address from, uint256 amount) public onlyOwner { _burn(from, amount); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import "solmate/auth/Owned.sol"; import "./lib/SafeERC20Namer.sol"; import "./Pair.sol"; /// @title caviar.sh /// @author out.eth (@outdoteth) /// @notice An AMM for creating and trading fractionalized NFTs. contract Caviar is Owned { using SafeERC20Namer for address; /// @dev pairs[nft][baseToken][merkleRoot] -> pair mapping(address => mapping(address => mapping(bytes32 => address))) public pairs; /// @dev The stolen nft filter oracle address 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; } /// @notice Sets the stolen nft filter oracle address. /// @param _stolenNftFilterOracle The stolen nft filter oracle address. function setStolenNftFilterOracle(address _stolenNftFilterOracle) public onlyOwner { stolenNftFilterOracle = _stolenNftFilterOracle; emit SetStolenNftFilterOracle(_stolenNftFilterOracle); } /// @notice Creates a new pair. /// @param nft The NFT contract address. /// @param baseToken The base token contract address. /// @param merkleRoot The merkle root for the valid tokenIds. /// @return pair The address of the new pair. function create(address nft, address baseToken, bytes32 merkleRoot) public returns (Pair pair) { // check that the pair doesn't already exist 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"); // deploy the pair 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); // save the pair pairs[nft][baseToken][merkleRoot] = address(pair); emit Create(nft, baseToken, merkleRoot); } /// @notice Deletes the pair for the given NFT, base token, and merkle root. /// @param nft The NFT contract address. /// @param baseToken The base token contract address. /// @param merkleRoot The merkle root for the valid tokenIds. function destroy(address nft, address baseToken, bytes32 merkleRoot) public { // check that a pair can only destroy itself require(msg.sender == pairs[nft][baseToken][merkleRoot], "Only pair can destroy itself"); // delete the pair delete pairs[nft][baseToken][merkleRoot]; emit Destroy(nft, baseToken, merkleRoot); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import "solmate/auth/Owned.sol"; import "reservoir-oracle/ReservoirOracle.sol"; /// @title StolenNftFilterOracle /// @author out.eth (@outdoteth) /// @notice A contract to check that a set of NFTs are not stolen. 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; mapping(address => bool) public isDisabled; constructor() Owned(msg.sender) ReservoirOracle(0xAeB1D03929bF87F69888f381e73FBf75753d75AF) {} /// @notice Sets the cooldown period. /// @param _cooldownPeriod The cooldown period. function setCooldownPeriod(uint256 _cooldownPeriod) public onlyOwner { cooldownPeriod = _cooldownPeriod; } /// @notice Sets the valid for period. /// @param _validFor The valid for period. function setValidFor(uint256 _validFor) public onlyOwner { validFor = _validFor; } /// @notice Updates the reservoir oracle address. /// @param newReservoirOracleAddress The new reservoir oracle address. function updateReservoirOracleAddress(address newReservoirOracleAddress) public override onlyOwner { RESERVOIR_ORACLE_ADDRESS = newReservoirOracleAddress; } /// @notice Sets whether a token validation is disabled. /// @param tokenAddress The token address. /// @param _isDisabled Whether the token validation is disabled. function setIsDisabled(address tokenAddress, bool _isDisabled) public onlyOwner { isDisabled[tokenAddress] = _isDisabled; } /// @notice Checks that a set of NFTs are not stolen. /// @param tokenAddress The address of the NFT contract. /// @param tokenIds The ids of the NFTs. /// @param messages The messages signed by the reservoir oracle. function validateTokensAreNotStolen(address tokenAddress, uint256[] calldata tokenIds, Message[] calldata messages) public view { if (isDisabled[tokenAddress]) return; for (uint256 i = 0; i < tokenIds.length; i++) { Message calldata message = messages[i]; // check that the signer is correct and message id matches token id + token address 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)); // check that the NFT is not stolen require(!isFlagged, "NFT is flagged as suspicious"); // check that the NFT was not transferred too recently require(lastTransferTime + cooldownPeriod < block.timestamp, "NFT was transferred too recently"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol) library MerkleProofLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MERKLE PROOF VERIFICATION OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`. function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool isValid) { /// @solidity memory-safe-assembly assembly { if mload(proof) { // Initialize `offset` to the offset of `proof` elements in memory. let offset := add(proof, 0x20) // Left shift by 5 is equivalent to multiplying by 0x20. let end := add(offset, shl(5, mload(proof))) // Iterate over proof elements to compute root hash. for {} 1 {} { // Slot of `leaf` in scratch space. // If the condition is true: 0x20, otherwise: 0x00. let scratch := shl(5, gt(leaf, mload(offset))) // Store elements to hash contiguously in scratch space. // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes. mstore(scratch, leaf) mstore(xor(scratch, 0x20), mload(offset)) // Reuse `leaf` to store the hash to reduce stack operations. leaf := keccak256(0x00, 0x40) offset := add(offset, 0x20) if iszero(lt(offset, end)) { break } } } isValid := eq(leaf, root) } } /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`. function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool isValid) { /// @solidity memory-safe-assembly assembly { if proof.length { // Left shift by 5 is equivalent to multiplying by 0x20. let end := add(proof.offset, shl(5, proof.length)) // Initialize `offset` to the offset of `proof` in the calldata. let offset := proof.offset // Iterate over proof elements to compute root hash. for {} 1 {} { // Slot of `leaf` in scratch space. // If the condition is true: 0x20, otherwise: 0x00. let scratch := shl(5, gt(leaf, calldataload(offset))) // Store elements to hash contiguously in scratch space. // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes. mstore(scratch, leaf) mstore(xor(scratch, 0x20), calldataload(offset)) // Reuse `leaf` to store the hash to reduce stack operations. leaf := keccak256(0x00, 0x40) offset := add(offset, 0x20) if iszero(lt(offset, end)) { break } } } isValid := eq(leaf, root) } } /// @dev Returns whether all `leafs` exist in the Merkle tree with `root`, /// given `proof` and `flags`. function verifyMultiProof( bytes32[] memory proof, bytes32 root, bytes32[] memory leafs, bool[] memory flags ) internal pure returns (bool isValid) { // Rebuilds the root by consuming and producing values on a queue. // The queue starts with the `leafs` array, and goes into a `hashes` array. // After the process, the last element on the queue is verified // to be equal to the `root`. // // The `flags` array denotes whether the sibling // should be popped from the queue (`flag == true`), or // should be popped from the `proof` (`flag == false`). /// @solidity memory-safe-assembly assembly { // Cache the lengths of the arrays. let leafsLength := mload(leafs) let proofLength := mload(proof) let flagsLength := mload(flags) // Advance the pointers of the arrays to point to the data. leafs := add(0x20, leafs) proof := add(0x20, proof) flags := add(0x20, flags) // If the number of flags is correct. for {} eq(add(leafsLength, proofLength), add(flagsLength, 1)) {} { // For the case where `proof.length + leafs.length == 1`. if iszero(flagsLength) { // `isValid = (proof.length == 1 ? proof[0] : leafs[0]) == root`. isValid := eq(mload(xor(leafs, mul(xor(proof, leafs), proofLength))), root) break } // We can use the free memory space for the queue. // We don't need to allocate, since the queue is temporary. let hashesFront := mload(0x40) // Copy the leafs into the hashes. // Sometimes, a little memory expansion costs less than branching. // Should cost less, even with a high free memory offset of 0x7d00. // Left shift by 5 is equivalent to multiplying by 0x20. leafsLength := shl(5, leafsLength) for { let i := 0 } iszero(eq(i, leafsLength)) { i := add(i, 0x20) } { mstore(add(hashesFront, i), mload(add(leafs, i))) } // Compute the back of the hashes. let hashesBack := add(hashesFront, leafsLength) // This is the end of the memory for the queue. // We recycle `flagsLength` to save on stack variables // (this trick may not always save gas). flagsLength := add(hashesBack, shl(5, flagsLength)) for {} 1 {} { // Pop from `hashes`. let a := mload(hashesFront) // Pop from `hashes`. let b := mload(add(hashesFront, 0x20)) hashesFront := add(hashesFront, 0x40) // If the flag is false, load the next proof, // else, pops from the queue. if iszero(mload(flags)) { // Loads the next proof. b := mload(proof) proof := add(proof, 0x20) // Unpop from `hashes`. hashesFront := sub(hashesFront, 0x20) } // Advance to the next flag. flags := add(flags, 0x20) // Slot of `a` in scratch space. // If the condition is true: 0x20, otherwise: 0x00. let scratch := shl(5, gt(a, b)) // Hash the scratch space and push the result onto the queue. mstore(scratch, a) mstore(xor(scratch, 0x20), b) mstore(hashesBack, keccak256(0x00, 0x40)) hashesBack := add(hashesBack, 0x20) if iszero(lt(hashesBack, flagsLength)) { break } } // Checks if the last value in the queue is same as the root. isValid := eq(mload(sub(hashesBack, 0x20)), root) break } } } /// @dev Returns whether all `leafs` exist in the Merkle tree with `root`, /// given `proof` and `flags`. function verifyMultiProofCalldata( bytes32[] calldata proof, bytes32 root, bytes32[] calldata leafs, bool[] calldata flags ) internal pure returns (bool isValid) { // Rebuilds the root by consuming and producing values on a queue. // The queue starts with the `leafs` array, and goes into a `hashes` array. // After the process, the last element on the queue is verified // to be equal to the `root`. // // The `flags` array denotes whether the sibling // should be popped from the queue (`flag == true`), or // should be popped from the `proof` (`flag == false`). /// @solidity memory-safe-assembly assembly { // If the number of flags is correct. for {} eq(add(leafs.length, proof.length), add(flags.length, 1)) {} { // For the case where `proof.length + leafs.length == 1`. if iszero(flags.length) { // `isValid = (proof.length == 1 ? proof[0] : leafs[0]) == root`. // forgefmt: disable-next-item isValid := eq( calldataload( xor(leafs.offset, mul(xor(proof.offset, leafs.offset), proof.length)) ), root ) break } // We can use the free memory space for the queue. // We don't need to allocate, since the queue is temporary. let hashesFront := mload(0x40) // Copy the leafs into the hashes. // Sometimes, a little memory expansion costs less than branching. // Should cost less, even with a high free memory offset of 0x7d00. // Left shift by 5 is equivalent to multiplying by 0x20. calldatacopy(hashesFront, leafs.offset, shl(5, leafs.length)) // Compute the back of the hashes. let hashesBack := add(hashesFront, shl(5, leafs.length)) // This is the end of the memory for the queue. // We recycle `flags.length` to save on stack variables // (this trick may not always save gas). flags.length := add(hashesBack, shl(5, flags.length)) // We don't need to make a copy of `proof.offset` or `flags.offset`, // as they are pass-by-value (this trick may not always save gas). for {} 1 {} { // Pop from `hashes`. let a := mload(hashesFront) // Pop from `hashes`. let b := mload(add(hashesFront, 0x20)) hashesFront := add(hashesFront, 0x40) // If the flag is false, load the next proof, // else, pops from the queue. if iszero(calldataload(flags.offset)) { // Loads the next proof. b := calldataload(proof.offset) proof.offset := add(proof.offset, 0x20) // Unpop from `hashes`. hashesFront := sub(hashesFront, 0x20) } // Advance to the next flag offset. flags.offset := add(flags.offset, 0x20) // Slot of `a` in scratch space. // If the condition is true: 0x20, otherwise: 0x00. let scratch := shl(5, gt(a, b)) // Hash the scratch space and push the result onto the queue. mstore(scratch, a) mstore(xor(scratch, 0x20), b) mstore(hashesBack, keccak256(0x00, 0x40)) hashesBack := add(hashesBack, 0x20) if iszero(lt(hashesBack, flags.length)) { break } } // Checks if the last value in the queue is same as the root. isValid := eq(mload(sub(hashesBack, 0x20)), root) break } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EMPTY CALLDATA HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns an empty calldata bytes32 array. function emptyProof() internal pure returns (bytes32[] calldata proof) { /// @solidity memory-safe-assembly assembly { proof.length := 0 } } /// @dev Returns an empty calldata bytes32 array. function emptyLeafs() internal pure returns (bytes32[] calldata leafs) { /// @solidity memory-safe-assembly assembly { leafs.length := 0 } } /// @dev Returns an empty calldata bool array. function emptyFlags() internal pure returns (bool[] calldata flags) { /// @solidity memory-safe-assembly assembly { flags.length := 0 } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol) // This file was procedurally generated from scripts/generate/templates/SafeCast.js. pragma solidity ^0.8.0; /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint248 from uint256, reverting on * overflow (when the input is greater than largest uint248). * * Counterpart to Solidity's `uint248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toUint248(uint256 value) internal pure returns (uint248) { require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); return uint248(value); } /** * @dev Returns the downcasted uint240 from uint256, reverting on * overflow (when the input is greater than largest uint240). * * Counterpart to Solidity's `uint240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toUint240(uint256 value) internal pure returns (uint240) { require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); return uint240(value); } /** * @dev Returns the downcasted uint232 from uint256, reverting on * overflow (when the input is greater than largest uint232). * * Counterpart to Solidity's `uint232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toUint232(uint256 value) internal pure returns (uint232) { require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); return uint232(value); } /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). * * Counterpart to Solidity's `uint224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.2._ */ function toUint224(uint256 value) internal pure returns (uint224) { require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); return uint224(value); } /** * @dev Returns the downcasted uint216 from uint256, reverting on * overflow (when the input is greater than largest uint216). * * Counterpart to Solidity's `uint216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toUint216(uint256 value) internal pure returns (uint216) { require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); return uint216(value); } /** * @dev Returns the downcasted uint208 from uint256, reverting on * overflow (when the input is greater than largest uint208). * * Counterpart to Solidity's `uint208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toUint208(uint256 value) internal pure returns (uint208) { require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); return uint208(value); } /** * @dev Returns the downcasted uint200 from uint256, reverting on * overflow (when the input is greater than largest uint200). * * Counterpart to Solidity's `uint200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toUint200(uint256 value) internal pure returns (uint200) { require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); return uint200(value); } /** * @dev Returns the downcasted uint192 from uint256, reverting on * overflow (when the input is greater than largest uint192). * * Counterpart to Solidity's `uint192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toUint192(uint256 value) internal pure returns (uint192) { require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); return uint192(value); } /** * @dev Returns the downcasted uint184 from uint256, reverting on * overflow (when the input is greater than largest uint184). * * Counterpart to Solidity's `uint184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toUint184(uint256 value) internal pure returns (uint184) { require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); return uint184(value); } /** * @dev Returns the downcasted uint176 from uint256, reverting on * overflow (when the input is greater than largest uint176). * * Counterpart to Solidity's `uint176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toUint176(uint256 value) internal pure returns (uint176) { require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); return uint176(value); } /** * @dev Returns the downcasted uint168 from uint256, reverting on * overflow (when the input is greater than largest uint168). * * Counterpart to Solidity's `uint168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toUint168(uint256 value) internal pure returns (uint168) { require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); return uint168(value); } /** * @dev Returns the downcasted uint160 from uint256, reverting on * overflow (when the input is greater than largest uint160). * * Counterpart to Solidity's `uint160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toUint160(uint256 value) internal pure returns (uint160) { require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); return uint160(value); } /** * @dev Returns the downcasted uint152 from uint256, reverting on * overflow (when the input is greater than largest uint152). * * Counterpart to Solidity's `uint152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toUint152(uint256 value) internal pure returns (uint152) { require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); return uint152(value); } /** * @dev Returns the downcasted uint144 from uint256, reverting on * overflow (when the input is greater than largest uint144). * * Counterpart to Solidity's `uint144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toUint144(uint256 value) internal pure returns (uint144) { require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); return uint144(value); } /** * @dev Returns the downcasted uint136 from uint256, reverting on * overflow (when the input is greater than largest uint136). * * Counterpart to Solidity's `uint136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toUint136(uint256 value) internal pure returns (uint136) { require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); return uint136(value); } /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v2.5._ */ function toUint128(uint256 value) internal pure returns (uint128) { require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint120 from uint256, reverting on * overflow (when the input is greater than largest uint120). * * Counterpart to Solidity's `uint120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toUint120(uint256 value) internal pure returns (uint120) { require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); return uint120(value); } /** * @dev Returns the downcasted uint112 from uint256, reverting on * overflow (when the input is greater than largest uint112). * * Counterpart to Solidity's `uint112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toUint112(uint256 value) internal pure returns (uint112) { require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); return uint112(value); } /** * @dev Returns the downcasted uint104 from uint256, reverting on * overflow (when the input is greater than largest uint104). * * Counterpart to Solidity's `uint104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toUint104(uint256 value) internal pure returns (uint104) { require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); return uint104(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.2._ */ function toUint96(uint256 value) internal pure returns (uint96) { require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); return uint96(value); } /** * @dev Returns the downcasted uint88 from uint256, reverting on * overflow (when the input is greater than largest uint88). * * Counterpart to Solidity's `uint88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toUint88(uint256 value) internal pure returns (uint88) { require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); return uint88(value); } /** * @dev Returns the downcasted uint80 from uint256, reverting on * overflow (when the input is greater than largest uint80). * * Counterpart to Solidity's `uint80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toUint80(uint256 value) internal pure returns (uint80) { require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); return uint80(value); } /** * @dev Returns the downcasted uint72 from uint256, reverting on * overflow (when the input is greater than largest uint72). * * Counterpart to Solidity's `uint72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toUint72(uint256 value) internal pure returns (uint72) { require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); return uint72(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v2.5._ */ function toUint64(uint256 value) internal pure returns (uint64) { require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint56 from uint256, reverting on * overflow (when the input is greater than largest uint56). * * Counterpart to Solidity's `uint56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toUint56(uint256 value) internal pure returns (uint56) { require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); return uint56(value); } /** * @dev Returns the downcasted uint48 from uint256, reverting on * overflow (when the input is greater than largest uint48). * * Counterpart to Solidity's `uint48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toUint48(uint256 value) internal pure returns (uint48) { require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); return uint48(value); } /** * @dev Returns the downcasted uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toUint40(uint256 value) internal pure returns (uint40) { require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); return uint40(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v2.5._ */ function toUint32(uint256 value) internal pure returns (uint32) { require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint24 from uint256, reverting on * overflow (when the input is greater than largest uint24). * * Counterpart to Solidity's `uint24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toUint24(uint256 value) internal pure returns (uint24) { require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); return uint24(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v2.5._ */ function toUint16(uint256 value) internal pure returns (uint16) { require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v2.5._ */ function toUint8(uint256 value) internal pure returns (uint8) { require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. * * _Available since v3.0._ */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Returns the downcasted int248 from int256, reverting on * overflow (when the input is less than smallest int248 or * greater than largest int248). * * Counterpart to Solidity's `int248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toInt248(int256 value) internal pure returns (int248 downcasted) { downcasted = int248(value); require(downcasted == value, "SafeCast: value doesn't fit in 248 bits"); } /** * @dev Returns the downcasted int240 from int256, reverting on * overflow (when the input is less than smallest int240 or * greater than largest int240). * * Counterpart to Solidity's `int240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toInt240(int256 value) internal pure returns (int240 downcasted) { downcasted = int240(value); require(downcasted == value, "SafeCast: value doesn't fit in 240 bits"); } /** * @dev Returns the downcasted int232 from int256, reverting on * overflow (when the input is less than smallest int232 or * greater than largest int232). * * Counterpart to Solidity's `int232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toInt232(int256 value) internal pure returns (int232 downcasted) { downcasted = int232(value); require(downcasted == value, "SafeCast: value doesn't fit in 232 bits"); } /** * @dev Returns the downcasted int224 from int256, reverting on * overflow (when the input is less than smallest int224 or * greater than largest int224). * * Counterpart to Solidity's `int224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.7._ */ function toInt224(int256 value) internal pure returns (int224 downcasted) { downcasted = int224(value); require(downcasted == value, "SafeCast: value doesn't fit in 224 bits"); } /** * @dev Returns the downcasted int216 from int256, reverting on * overflow (when the input is less than smallest int216 or * greater than largest int216). * * Counterpart to Solidity's `int216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toInt216(int256 value) internal pure returns (int216 downcasted) { downcasted = int216(value); require(downcasted == value, "SafeCast: value doesn't fit in 216 bits"); } /** * @dev Returns the downcasted int208 from int256, reverting on * overflow (when the input is less than smallest int208 or * greater than largest int208). * * Counterpart to Solidity's `int208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toInt208(int256 value) internal pure returns (int208 downcasted) { downcasted = int208(value); require(downcasted == value, "SafeCast: value doesn't fit in 208 bits"); } /** * @dev Returns the downcasted int200 from int256, reverting on * overflow (when the input is less than smallest int200 or * greater than largest int200). * * Counterpart to Solidity's `int200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toInt200(int256 value) internal pure returns (int200 downcasted) { downcasted = int200(value); require(downcasted == value, "SafeCast: value doesn't fit in 200 bits"); } /** * @dev Returns the downcasted int192 from int256, reverting on * overflow (when the input is less than smallest int192 or * greater than largest int192). * * Counterpart to Solidity's `int192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toInt192(int256 value) internal pure returns (int192 downcasted) { downcasted = int192(value); require(downcasted == value, "SafeCast: value doesn't fit in 192 bits"); } /** * @dev Returns the downcasted int184 from int256, reverting on * overflow (when the input is less than smallest int184 or * greater than largest int184). * * Counterpart to Solidity's `int184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toInt184(int256 value) internal pure returns (int184 downcasted) { downcasted = int184(value); require(downcasted == value, "SafeCast: value doesn't fit in 184 bits"); } /** * @dev Returns the downcasted int176 from int256, reverting on * overflow (when the input is less than smallest int176 or * greater than largest int176). * * Counterpart to Solidity's `int176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toInt176(int256 value) internal pure returns (int176 downcasted) { downcasted = int176(value); require(downcasted == value, "SafeCast: value doesn't fit in 176 bits"); } /** * @dev Returns the downcasted int168 from int256, reverting on * overflow (when the input is less than smallest int168 or * greater than largest int168). * * Counterpart to Solidity's `int168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toInt168(int256 value) internal pure returns (int168 downcasted) { downcasted = int168(value); require(downcasted == value, "SafeCast: value doesn't fit in 168 bits"); } /** * @dev Returns the downcasted int160 from int256, reverting on * overflow (when the input is less than smallest int160 or * greater than largest int160). * * Counterpart to Solidity's `int160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toInt160(int256 value) internal pure returns (int160 downcasted) { downcasted = int160(value); require(downcasted == value, "SafeCast: value doesn't fit in 160 bits"); } /** * @dev Returns the downcasted int152 from int256, reverting on * overflow (when the input is less than smallest int152 or * greater than largest int152). * * Counterpart to Solidity's `int152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toInt152(int256 value) internal pure returns (int152 downcasted) { downcasted = int152(value); require(downcasted == value, "SafeCast: value doesn't fit in 152 bits"); } /** * @dev Returns the downcasted int144 from int256, reverting on * overflow (when the input is less than smallest int144 or * greater than largest int144). * * Counterpart to Solidity's `int144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toInt144(int256 value) internal pure returns (int144 downcasted) { downcasted = int144(value); require(downcasted == value, "SafeCast: value doesn't fit in 144 bits"); } /** * @dev Returns the downcasted int136 from int256, reverting on * overflow (when the input is less than smallest int136 or * greater than largest int136). * * Counterpart to Solidity's `int136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toInt136(int256 value) internal pure returns (int136 downcasted) { downcasted = int136(value); require(downcasted == value, "SafeCast: value doesn't fit in 136 bits"); } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128 downcasted) { downcasted = int128(value); require(downcasted == value, "SafeCast: value doesn't fit in 128 bits"); } /** * @dev Returns the downcasted int120 from int256, reverting on * overflow (when the input is less than smallest int120 or * greater than largest int120). * * Counterpart to Solidity's `int120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toInt120(int256 value) internal pure returns (int120 downcasted) { downcasted = int120(value); require(downcasted == value, "SafeCast: value doesn't fit in 120 bits"); } /** * @dev Returns the downcasted int112 from int256, reverting on * overflow (when the input is less than smallest int112 or * greater than largest int112). * * Counterpart to Solidity's `int112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toInt112(int256 value) internal pure returns (int112 downcasted) { downcasted = int112(value); require(downcasted == value, "SafeCast: value doesn't fit in 112 bits"); } /** * @dev Returns the downcasted int104 from int256, reverting on * overflow (when the input is less than smallest int104 or * greater than largest int104). * * Counterpart to Solidity's `int104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toInt104(int256 value) internal pure returns (int104 downcasted) { downcasted = int104(value); require(downcasted == value, "SafeCast: value doesn't fit in 104 bits"); } /** * @dev Returns the downcasted int96 from int256, reverting on * overflow (when the input is less than smallest int96 or * greater than largest int96). * * Counterpart to Solidity's `int96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.7._ */ function toInt96(int256 value) internal pure returns (int96 downcasted) { downcasted = int96(value); require(downcasted == value, "SafeCast: value doesn't fit in 96 bits"); } /** * @dev Returns the downcasted int88 from int256, reverting on * overflow (when the input is less than smallest int88 or * greater than largest int88). * * Counterpart to Solidity's `int88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toInt88(int256 value) internal pure returns (int88 downcasted) { downcasted = int88(value); require(downcasted == value, "SafeCast: value doesn't fit in 88 bits"); } /** * @dev Returns the downcasted int80 from int256, reverting on * overflow (when the input is less than smallest int80 or * greater than largest int80). * * Counterpart to Solidity's `int80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toInt80(int256 value) internal pure returns (int80 downcasted) { downcasted = int80(value); require(downcasted == value, "SafeCast: value doesn't fit in 80 bits"); } /** * @dev Returns the downcasted int72 from int256, reverting on * overflow (when the input is less than smallest int72 or * greater than largest int72). * * Counterpart to Solidity's `int72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toInt72(int256 value) internal pure returns (int72 downcasted) { downcasted = int72(value); require(downcasted == value, "SafeCast: value doesn't fit in 72 bits"); } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64 downcasted) { downcasted = int64(value); require(downcasted == value, "SafeCast: value doesn't fit in 64 bits"); } /** * @dev Returns the downcasted int56 from int256, reverting on * overflow (when the input is less than smallest int56 or * greater than largest int56). * * Counterpart to Solidity's `int56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toInt56(int256 value) internal pure returns (int56 downcasted) { downcasted = int56(value); require(downcasted == value, "SafeCast: value doesn't fit in 56 bits"); } /** * @dev Returns the downcasted int48 from int256, reverting on * overflow (when the input is less than smallest int48 or * greater than largest int48). * * Counterpart to Solidity's `int48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toInt48(int256 value) internal pure returns (int48 downcasted) { downcasted = int48(value); require(downcasted == value, "SafeCast: value doesn't fit in 48 bits"); } /** * @dev Returns the downcasted int40 from int256, reverting on * overflow (when the input is less than smallest int40 or * greater than largest int40). * * Counterpart to Solidity's `int40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toInt40(int256 value) internal pure returns (int40 downcasted) { downcasted = int40(value); require(downcasted == value, "SafeCast: value doesn't fit in 40 bits"); } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32 downcasted) { downcasted = int32(value); require(downcasted == value, "SafeCast: value doesn't fit in 32 bits"); } /** * @dev Returns the downcasted int24 from int256, reverting on * overflow (when the input is less than smallest int24 or * greater than largest int24). * * Counterpart to Solidity's `int24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toInt24(int256 value) internal pure returns (int24 downcasted) { downcasted = int24(value); require(downcasted == value, "SafeCast: value doesn't fit in 24 bits"); } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16 downcasted) { downcasted = int16(value); require(downcasted == value, "SafeCast: value doesn't fit in 16 bits"); } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8 downcasted) { downcasted = int8(value); require(downcasted == value, "SafeCast: value doesn't fit in 8 bits"); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. * * _Available since v3.0._ */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); return int256(value); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC3156FlashLender.sol) pragma solidity ^0.8.0; import "./IERC3156FlashBorrower.sol"; /** * @dev Interface of the ERC3156 FlashLender, as defined in * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. * * _Available since v4.1._ */ interface IERC3156FlashLender { /** * @dev The amount of currency available to be lended. * @param token The loan currency. * @return The amount of `token` that can be borrowed. */ function maxFlashLoan(address token) external view returns (uint256); /** * @dev The fee to be charged for a given loan. * @param token The loan currency. * @param amount The amount of tokens lent. * @return The amount of `token` to be charged for the loan, on top of the returned principal. */ function flashFee(address token, uint256 amount) external view returns (uint256); /** * @dev Initiate a flash loan. * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. * @param token The loan currency. * @param amount The amount of tokens lent. * @param data Arbitrary data structure, intended to contain user-defined parameters. */ function flashLoan( IERC3156FlashBorrower receiver, address token, uint256 amount, bytes calldata data ) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; /* * * __________...----..____..-'``-..___ * ,'. ```--.._ * : ``._ * | -- ``. * | -0- -. - -. `. * : __ -- . \ * `._____________ ( `. -.- -- - . ` \ * `-----------------\ \_.--------..__..--.._ `. `. : * `--' `-._ . | * `.` | * \` | * \ | * / \`. * / _\-' * /_,' */ import {LibClone} from "solady/utils/LibClone.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; import {ERC721} from "solmate/tokens/ERC721.sol"; import {Owned} from "solmate/auth/Owned.sol"; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {PrivatePool} from "./PrivatePool.sol"; import {PrivatePoolMetadata} from "./PrivatePoolMetadata.sol"; /// @title Caviar Private Pool Factory /// @author out.eth (@outdoteth) /// @notice This contract is used to create and initialize new private pools. Each time a private pool is created, a new /// NFT representing that private pool is minted to the creator. All protocol fees also accrue to this contract and can /// be withdrawn by the admin. contract Factory is ERC721, Owned { using LibClone for address; using SafeTransferLib for address; event Create(address indexed privatePool, uint256[] tokenIds, uint256 baseTokenAmount); event Withdraw(address indexed token, uint256 indexed amount); event SetPrivatePoolMetadata(address indexed privatePoolMetadata); event SetPrivatePoolImplementation(address indexed privatePoolImplementation); event SetProtocolFeeRate(uint16 indexed protocolFeeRate); event SetProtocolChangeFeeRate(uint16 indexed protocolChangeFeeRate); error ProtocolFeeRateTooHigh(); error ProtocolChangeFeeRateTooHigh(); error URIQueryForNonExistentToken(); /// @notice The address of the private pool implementation that proxies point to. address public privatePoolImplementation; /// @notice Helper contract that constructs the private pool metadata svg and json for each pool NFT. address public privatePoolMetadata; /// @notice The protocol fee that is taken on each buy/sell/change. It's in basis points: 350 = 3.5%. uint16 public protocolFeeRate; /// @notice The protocol change fee rate that is taken on each change/flash loan. It's in basis points: 200 = 2.00%. uint16 public protocolChangeFeeRate; constructor() ERC721("Caviar Private Pools", "POOL") Owned(msg.sender) {} receive() external payable {} /// @notice Creates a new private pool using the minimal proxy pattern that points to the private pool /// implementation. The caller must approve the factory to transfer the NFTs that will be deposited to the pool. /// @param _baseToken The address of the base token. /// @param _nft The address of the NFT. /// @param _virtualBaseTokenReserves The virtual base token reserves. /// @param _virtualNftReserves The virtual NFT reserves. /// @param _changeFee The change fee. /// @param _feeRate The fee rate. /// @param _merkleRoot The merkle root. /// @param _useStolenNftOracle Whether to use the stolen NFT oracle. /// @param _salt The salt that will used on deployment. /// @param tokenIds The token ids to deposit to the pool. /// @param baseTokenAmount The amount of base tokens to deposit to the pool. /// @return privatePool The address of the private pool. function create( address _baseToken, address _nft, uint128 _virtualBaseTokenReserves, uint128 _virtualNftReserves, uint56 _changeFee, uint16 _feeRate, bytes32 _merkleRoot, bool _useStolenNftOracle, bool _payRoyalties, bytes32 _salt, uint256[] memory tokenIds, // put in memory to avoid stack too deep error uint256 baseTokenAmount ) public payable returns (PrivatePool privatePool) { // check that the msg.value is equal to the base token amount if the base token is ETH or the msg.value is equal // to zero if the base token is not ETH if ((_baseToken == address(0) && msg.value != baseTokenAmount) || (_baseToken != address(0) && msg.value > 0)) { revert PrivatePool.InvalidEthAmount(); } // deploy a minimal proxy clone of the private pool implementation bytes32 salt = keccak256(abi.encode(msg.sender, _salt)); privatePool = PrivatePool(payable(privatePoolImplementation.cloneDeterministic(salt))); // mint the nft to the caller _safeMint(msg.sender, uint256(uint160(address(privatePool)))); // initialize the pool privatePool.initialize( _baseToken, _nft, _virtualBaseTokenReserves, _virtualNftReserves, _changeFee, _feeRate, _merkleRoot, _useStolenNftOracle, _payRoyalties ); if (_baseToken == address(0)) { // transfer eth into the pool if base token is ETH address(privatePool).safeTransferETH(baseTokenAmount); } else { // deposit the base tokens from the caller into the pool SafeTransferLib.safeTransferFrom(ERC20(_baseToken), msg.sender, address(privatePool), baseTokenAmount); } // deposit the nfts from the caller into the pool for (uint256 i = 0; i < tokenIds.length; i++) { ERC721(_nft).safeTransferFrom(msg.sender, address(privatePool), tokenIds[i]); } // emit create event emit Create(address(privatePool), tokenIds, baseTokenAmount); } /// @notice Sets private pool metadata contract. /// @param _privatePoolMetadata The private pool metadata contract. function setPrivatePoolMetadata(address _privatePoolMetadata) public onlyOwner { privatePoolMetadata = _privatePoolMetadata; emit SetPrivatePoolMetadata(_privatePoolMetadata); } /// @notice Sets the private pool implementation contract that newly deployed proxies point to. /// @param _privatePoolImplementation The private pool implementation contract. function setPrivatePoolImplementation(address _privatePoolImplementation) public onlyOwner { privatePoolImplementation = _privatePoolImplementation; emit SetPrivatePoolImplementation(_privatePoolImplementation); } /// @notice Sets the protocol fee that is taken on each buy/sell/change. It's in basis points: 350 = 3.5%. /// @param _protocolFeeRate The protocol fee. function setProtocolFeeRate(uint16 _protocolFeeRate) public onlyOwner { // check that the protocol fee rate is not higher than 5% if (_protocolFeeRate > 500) revert ProtocolFeeRateTooHigh(); protocolFeeRate = _protocolFeeRate; emit SetProtocolFeeRate(_protocolFeeRate); } /// @notice Sets the protocol fee that is taken on change or flash loan. It's in basis points: 350 = 3.5%. /// @param _protocolChangeFeeRate The protocol change fee rate. function setProtocolChangeFeeRate(uint16 _protocolChangeFeeRate) public onlyOwner { // check that the protocol change fee rate is not higher than 100% if (_protocolChangeFeeRate > 10_000) revert ProtocolChangeFeeRateTooHigh(); protocolChangeFeeRate = _protocolChangeFeeRate; emit SetProtocolChangeFeeRate(_protocolChangeFeeRate); } /// @notice Withdraws the earned protocol fees. /// @param token The token to withdraw. /// @param amount The amount to withdraw. function withdraw(address token, uint256 amount) public onlyOwner { if (token == address(0)) { msg.sender.safeTransferETH(amount); } else { ERC20(token).transfer(msg.sender, amount); } emit Withdraw(token, amount); } /// @notice Returns the token URI for a given token id. /// @param id The token id. /// @return uri The token URI. function tokenURI(uint256 id) public view override returns (string memory) { // check that the token exists if (_ownerOf[id] == address(0)) revert URIQueryForNonExistentToken(); return PrivatePoolMetadata(privatePoolMetadata).tokenURI(id); } /// @notice Predicts the deployment address of a new private pool. /// @param salt The salt that will used on deployment. /// @return predictedAddress The predicted deployment address of the private pool. function predictPoolDeploymentAddress(bytes32 salt) public view returns (address predictedAddress) { predictedAddress = privatePoolImplementation.predictDeterministicAddress(salt, address(this)); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Simple single owner authorization mixin. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol) abstract contract Owned { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event OwnershipTransferred(address indexed user, address indexed newOwner); /*////////////////////////////////////////////////////////////// OWNERSHIP STORAGE //////////////////////////////////////////////////////////////*/ address public owner; modifier onlyOwner() virtual { require(msg.sender == owner, "UNAUTHORIZED"); _; } /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(address _owner) { owner = _owner; emit OwnershipTransferred(address(0), _owner); } /*////////////////////////////////////////////////////////////// OWNERSHIP LOGIC //////////////////////////////////////////////////////////////*/ function transferOwnership(address newOwner) public virtual onlyOwner { owner = newOwner; emit OwnershipTransferred(msg.sender, newOwner); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import "openzeppelin/utils/Strings.sol"; // modified from https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/SafeERC20Namer.sol // produces token descriptors from inconsistent or absent ERC20 symbol implementations that can return string or bytes32 // this library will always produce a string symbol to represent the token 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); } // uses a heuristic to produce a token name from the address // the heuristic returns the full hex of the address string function addressToName(address token) private pure returns (string memory) { return Strings.toHexString(uint160(token)); } // uses a heuristic to produce a token symbol from the address // the heuristic returns the first 4 hex of the address string function addressToSymbol(address token) private pure returns (string memory) { return Strings.toHexString(uint160(token) >> (160 - 4 * 4)); } // calls an external view token contract method that returns a symbol or name, and parses the output into a string function callAndParseStringReturn(address token, bytes4 selector) private view returns (string memory) { (bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(selector)); // if not implemented, or returns empty data, return empty string if (!success || data.length == 0) { return ""; } // bytes32 data always has length 32 if (data.length == 32) { bytes32 decoded = abi.decode(data, (bytes32)); return bytes32ToString(decoded); } else if (data.length > 64) { return abi.decode(data, (string)); } return ""; } // attempts to extract the token symbol. if it does not implement symbol, returns a symbol derived from the address function tokenSymbol(address token) internal view returns (string memory) { // 0x95d89b41 = bytes4(keccak256("symbol()")) string memory symbol = callAndParseStringReturn(token, 0x95d89b41); if (bytes(symbol).length == 0) { // fallback to 6 uppercase hex of address return addressToSymbol(token); } return symbol; } // attempts to extract the token name. if it does not implement name, returns a name derived from the address function tokenName(address token) internal view returns (string memory) { // 0x06fdde03 = bytes4(keccak256("name()")) string memory name = callAndParseStringReturn(token, 0x06fdde03); if (bytes(name).length == 0) { // fallback to full hex of address return addressToName(token); } return name; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (interfaces/IERC3156FlashBorrower.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC3156 FlashBorrower, as defined in * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. * * _Available since v4.1._ */ interface IERC3156FlashBorrower { /** * @dev Receive a flash loan. * @param initiator The initiator of the loan. * @param token The loan currency. * @param amount The amount of tokens lent. * @param fee The additional amount of tokens to repay. * @param data Arbitrary data structure, intended to contain user-defined parameters. * @return The keccak256 hash of "IERC3156FlashBorrower.onFlashLoan" */ function onFlashLoan( address initiator, address token, uint256 amount, uint256 fee, bytes calldata data ) external returns (bytes32); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Minimal proxy library. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibClone.sol) /// @author Minimal proxy by 0age (https://github.com/0age) /// @author Clones with immutable args by wighawag, zefram.eth, Saw-mon & Natalie /// (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args) /// /// @dev Minimal proxy: /// Although the sw0nt pattern saves 5 gas over the erc-1167 pattern during runtime, /// it is not supported out-of-the-box on Etherscan. Hence, we choose to use the 0age pattern, /// which saves 4 gas over the erc-1167 pattern during runtime, and has the smallest bytecode. /// /// @dev Clones with immutable args (CWIA): /// The implementation of CWIA here implements a `receive()` method that emits the /// `ReceiveETH(uint256)` event. This skips the `DELEGATECALL` when there is no calldata, /// enabling us to accept hard gas-capped `sends` & `transfers` for maximum backwards /// composability. The minimal proxy implementation does not offer this feature. library LibClone { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Unable to deploy the clone. error DeploymentFailed(); /// @dev The salt must start with either the zero address or the caller. error SaltDoesNotStartWithCaller(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MINIMAL PROXY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Deploys a clone of `implementation`. function clone(address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { /** * --------------------------------------------------------------------------+ * CREATION (9 bytes) | * --------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * --------------------------------------------------------------------------| * 60 runSize | PUSH1 runSize | r | | * 3d | RETURNDATASIZE | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 3d | RETURNDATASIZE | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * --------------------------------------------------------------------------| * RUNTIME (44 bytes) | * --------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * --------------------------------------------------------------------------| * | * ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | 0 | | * 3d | RETURNDATASIZE | 0 0 | | * 3d | RETURNDATASIZE | 0 0 0 | | * 3d | RETURNDATASIZE | 0 0 0 0 | | * | * ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds 0 0 0 0 | | * 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | | * 3d | RETURNDATASIZE | 0 0 cds 0 0 0 0 | | * 37 | CALLDATACOPY | 0 0 0 0 | [0..cds): calldata | * | * ::: delegate call to the implementation contract :::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds 0 0 0 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | [0..cds): calldata | * 73 addr | PUSH20 addr | addr 0 cds 0 0 0 0 | [0..cds): calldata | * 5a | GAS | gas addr 0 cds 0 0 0 0 | [0..cds): calldata | * f4 | DELEGATECALL | success 0 0 | [0..cds): calldata | * | * ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds success 0 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | rds rds success 0 0 | [0..cds): calldata | * 93 | SWAP4 | 0 rds success 0 rds | [0..cds): calldata | * 80 | DUP1 | 0 0 rds success 0 rds | [0..cds): calldata | * 3e | RETURNDATACOPY | success 0 rds | [0..rds): returndata | * | * 60 0x2a | PUSH1 0x2a | 0x2a success 0 rds | [0..rds): returndata | * 57 | JUMPI | 0 rds | [0..rds): returndata | * | * ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * fd | REVERT | | [0..rds): returndata | * | * ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | 0 rds | [0..rds): returndata | * f3 | RETURN | | [0..rds): returndata | * --------------------------------------------------------------------------+ */ mstore(0x21, 0x5af43d3d93803e602a57fd5bf3) mstore(0x14, implementation) mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) instance := create(0, 0x0c, 0x35) // Restore the part of the free memory pointer that has been overwritten. mstore(0x21, 0) // If `instance` is zero, revert. if iszero(instance) { // Store the function selector of `DeploymentFailed()`. mstore(0x00, 0x30116425) // Revert with (offset, size). revert(0x1c, 0x04) } } } /// @dev Deploys a deterministic clone of `implementation` with `salt`. function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { mstore(0x21, 0x5af43d3d93803e602a57fd5bf3) mstore(0x14, implementation) mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) instance := create2(0, 0x0c, 0x35, salt) // Restore the part of the free memory pointer that has been overwritten. mstore(0x21, 0) // If `instance` is zero, revert. if iszero(instance) { // Store the function selector of `DeploymentFailed()`. mstore(0x00, 0x30116425) // Revert with (offset, size). revert(0x1c, 0x04) } } } /// @dev Returns the initialization code hash of the clone of `implementation`. /// Used for mining vanity addresses with create2crunch. function initCodeHash(address implementation) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { mstore(0x21, 0x5af43d3d93803e602a57fd5bf3) mstore(0x14, implementation) mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) hash := keccak256(0x0c, 0x35) // Restore the part of the free memory pointer that has been overwritten. mstore(0x21, 0) } } /// @dev Returns the address of the deterministic clone of `implementation`, /// with `salt` by `deployer`. function predictDeterministicAddress(address implementation, bytes32 salt, address deployer) internal pure returns (address predicted) { bytes32 hash = initCodeHash(implementation); predicted = predictDeterministicAddress(hash, salt, deployer); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CLONES WITH IMMUTABLE ARGS OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Deploys a minimal proxy with `implementation`, /// using immutable arguments encoded in `data`. function clone(address implementation, bytes memory data) internal returns (address instance) { assembly { // Compute the boundaries of the data and cache the memory slots around it. let mBefore3 := mload(sub(data, 0x60)) let mBefore2 := mload(sub(data, 0x40)) let mBefore1 := mload(sub(data, 0x20)) let dataLength := mload(data) let dataEnd := add(add(data, 0x20), dataLength) let mAfter1 := mload(dataEnd) // +2 bytes for telling how much data there is appended to the call. let extraLength := add(dataLength, 2) // The `creationSize` is `extraLength + 108` // The `runSize` is `creationSize - 10`. /** * ---------------------------------------------------------------------------------------------------+ * CREATION (10 bytes) | * ---------------------------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------------------------| * 61 runSize | PUSH2 runSize | r | | * 3d | RETURNDATASIZE | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 3d | RETURNDATASIZE | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * ---------------------------------------------------------------------------------------------------| * RUNTIME (98 bytes + extraLength) | * ---------------------------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------------------------| * | * ::: if no calldata, emit event & return w/o `DELEGATECALL` ::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds | | * 60 0x2c | PUSH1 0x2c | 0x2c cds | | * 57 | JUMPI | | | * 34 | CALLVALUE | cv | | * 3d | RETURNDATASIZE | 0 cv | | * 52 | MSTORE | | [0..0x20): callvalue | * 7f sig | PUSH32 0x9e.. | sig | [0..0x20): callvalue | * 59 | MSIZE | 0x20 sig | [0..0x20): callvalue | * 3d | RETURNDATASIZE | 0 0x20 sig | [0..0x20): callvalue | * a1 | LOG1 | | [0..0x20): callvalue | * 00 | STOP | | [0..0x20): callvalue | * 5b | JUMPDEST | | | * | * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds | | * 3d | RETURNDATASIZE | 0 cds | | * 3d | RETURNDATASIZE | 0 0 cds | | * 37 | CALLDATACOPY | | [0..cds): calldata | * | * ::: keep some values in stack :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | 0 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | 0 0 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | 0 0 0 0 | [0..cds): calldata | * 61 extra | PUSH2 extra | e 0 0 0 0 | [0..cds): calldata | * | * ::: copy extra data to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 80 | DUP1 | e e 0 0 0 0 | [0..cds): calldata | * 60 0x62 | PUSH1 0x62 | 0x62 e e 0 0 0 0 | [0..cds): calldata | * 36 | CALLDATASIZE | cds 0x62 e e 0 0 0 0 | [0..cds): calldata | * 39 | CODECOPY | e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * | * ::: delegate call to the implementation contract ::::::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 01 | ADD | cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 3d | RETURNDATASIZE | 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 73 addr | PUSH20 addr | addr 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 5a | GAS | gas addr 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * f4 | DELEGATECALL | success 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * | * ::: copy return data to memory ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds success 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 3d | RETURNDATASIZE | rds rds success 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 93 | SWAP4 | 0 rds success 0 rds | [0..cds): calldata, [cds..cds+e): extraData | * 80 | DUP1 | 0 0 rds success 0 rds | [0..cds): calldata, [cds..cds+e): extraData | * 3e | RETURNDATACOPY | success 0 rds | [0..rds): returndata | * | * 60 0x60 | PUSH1 0x60 | 0x60 success 0 rds | [0..rds): returndata | * 57 | JUMPI | 0 rds | [0..rds): returndata | * | * ::: revert ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * fd | REVERT | | [0..rds): returndata | * | * ::: return ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | 0 rds | [0..rds): returndata | * f3 | RETURN | | [0..rds): returndata | * ---------------------------------------------------------------------------------------------------+ */ // Write the bytecode before the data. mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the address of the implementation. mstore(sub(data, 0x0d), implementation) // Write the rest of the bytecode. mstore( sub(data, 0x21), or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73) ) // `keccak256("ReceiveETH(uint256)")` mstore( sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff ) mstore( sub(data, 0x5a), or(shl(0x78, add(extraLength, 0x62)), 0x6100003d81600a3d39f336602c57343d527f) ) mstore(dataEnd, shl(0xf0, extraLength)) // Create the instance. instance := create(0, sub(data, 0x4c), add(extraLength, 0x6c)) // If `instance` is zero, revert. if iszero(instance) { // Store the function selector of `DeploymentFailed()`. mstore(0x00, 0x30116425) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the overwritten memory surrounding `data`. mstore(dataEnd, mAfter1) mstore(data, dataLength) mstore(sub(data, 0x20), mBefore1) mstore(sub(data, 0x40), mBefore2) mstore(sub(data, 0x60), mBefore3) } } /// @dev Deploys a deterministic clone of `implementation`, /// using immutable arguments encoded in `data`, with `salt`. function cloneDeterministic(address implementation, bytes memory data, bytes32 salt) internal returns (address instance) { assembly { // Compute the boundaries of the data and cache the memory slots around it. let mBefore3 := mload(sub(data, 0x60)) let mBefore2 := mload(sub(data, 0x40)) let mBefore1 := mload(sub(data, 0x20)) let dataLength := mload(data) let dataEnd := add(add(data, 0x20), dataLength) let mAfter1 := mload(dataEnd) // +2 bytes for telling how much data there is appended to the call. let extraLength := add(dataLength, 2) // Write the bytecode before the data. mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the address of the implementation. mstore(sub(data, 0x0d), implementation) // Write the rest of the bytecode. mstore( sub(data, 0x21), or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73) ) // `keccak256("ReceiveETH(uint256)")` mstore( sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff ) mstore( sub(data, 0x5a), or(shl(0x78, add(extraLength, 0x62)), 0x6100003d81600a3d39f336602c57343d527f) ) mstore(dataEnd, shl(0xf0, extraLength)) // Create the instance. instance := create2(0, sub(data, 0x4c), add(extraLength, 0x6c), salt) // If `instance` is zero, revert. if iszero(instance) { // Store the function selector of `DeploymentFailed()`. mstore(0x00, 0x30116425) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the overwritten memory surrounding `data`. mstore(dataEnd, mAfter1) mstore(data, dataLength) mstore(sub(data, 0x20), mBefore1) mstore(sub(data, 0x40), mBefore2) mstore(sub(data, 0x60), mBefore3) } } /// @dev Returns the initialization code hash of the clone of `implementation` /// using immutable arguments encoded in `data`. /// Used for mining vanity addresses with create2crunch. function initCodeHash(address implementation, bytes memory data) internal pure returns (bytes32 hash) { assembly { // Compute the boundaries of the data and cache the memory slots around it. let mBefore3 := mload(sub(data, 0x60)) let mBefore2 := mload(sub(data, 0x40)) let mBefore1 := mload(sub(data, 0x20)) let dataLength := mload(data) let dataEnd := add(add(data, 0x20), dataLength) let mAfter1 := mload(dataEnd) // +2 bytes for telling how much data there is appended to the call. let extraLength := add(dataLength, 2) // Write the bytecode before the data. mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the address of the implementation. mstore(sub(data, 0x0d), implementation) // Write the rest of the bytecode. mstore( sub(data, 0x21), or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73) ) // `keccak256("ReceiveETH(uint256)")` mstore( sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff ) mstore( sub(data, 0x5a), or(shl(0x78, add(extraLength, 0x62)), 0x6100003d81600a3d39f336602c57343d527f) ) mstore(dataEnd, shl(0xf0, extraLength)) // Compute and store the bytecode hash. hash := keccak256(sub(data, 0x4c), add(extraLength, 0x6c)) // Restore the overwritten memory surrounding `data`. mstore(dataEnd, mAfter1) mstore(data, dataLength) mstore(sub(data, 0x20), mBefore1) mstore(sub(data, 0x40), mBefore2) mstore(sub(data, 0x60), mBefore3) } } /// @dev Returns the address of the deterministic clone of /// `implementation` using immutable arguments encoded in `data`, with `salt`, by `deployer`. function predictDeterministicAddress( address implementation, bytes memory data, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHash(implementation, data); predicted = predictDeterministicAddress(hash, salt, deployer); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* OTHER OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the address when a contract with initialization code hash, /// `hash`, is deployed with `salt`, by `deployer`. function predictDeterministicAddress(bytes32 hash, bytes32 salt, address deployer) internal pure returns (address predicted) { /// @solidity memory-safe-assembly assembly { // Compute and store the bytecode hash. mstore8(0x00, 0xff) // Write the prefix. mstore(0x35, hash) mstore(0x01, shl(96, deployer)) mstore(0x15, salt) predicted := keccak256(0x00, 0x55) // Restore the part of the free memory pointer that has been overwritten. mstore(0x35, 0) } } /// @dev Reverts if `salt` does not start with either the zero address or the caller. function checkStartsWithCaller(bytes32 salt) internal view { /// @solidity memory-safe-assembly assembly { // If the salt does not start with the zero address or the caller. if iszero(or(iszero(shr(96, salt)), eq(caller(), shr(96, salt)))) { // Store the function selector of `SaltDoesNotStartWithCaller()`. mstore(0x00, 0x2f634836) // Revert with (offset, size). revert(0x1c, 0x04) } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {Strings} from "openzeppelin/utils/Strings.sol"; import {Base64} from "openzeppelin/utils/Base64.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; import {ERC721} from "solmate/tokens/ERC721.sol"; import {PrivatePool} from "./PrivatePool.sol"; /// @title Private Pool Metadata /// @author out.eth (@outdoteth) /// @notice This contract is used to generate NFT metadata for private pools. contract PrivatePoolMetadata { /// @notice Returns the tokenURI for a pool with it's metadata. /// @param tokenId The private pool's token ID. function tokenURI(uint256 tokenId) public view returns (string memory) { // forgefmt: disable-next-item bytes memory metadata = abi.encodePacked( "{", '"name": "Private Pool ',Strings.toHexString(tokenId),'",', '"description": "Caviar private pool AMM position.",', '"image": ','"data:image/svg+xml;base64,', Base64.encode(svg(tokenId)),'",', '"attributes": [', attributes(tokenId), "]", "}" ); return string(abi.encodePacked("data:application/json;base64,", Base64.encode(metadata))); } /// @notice Returns the attributes for a pool encoded as json. /// @param tokenId The private pool's token ID. function attributes(uint256 tokenId) public view returns (string memory) { PrivatePool privatePool = PrivatePool(payable(address(uint160(tokenId)))); // forgefmt: disable-next-item bytes memory _attributes = abi.encodePacked( _trait("Pool address", Strings.toHexString(address(privatePool))), ',', _trait("Base token", Strings.toHexString(privatePool.baseToken())), ',', _trait("NFT", Strings.toHexString(privatePool.nft())), ',', _trait("Virtual base token reserves",Strings.toString(privatePool.virtualBaseTokenReserves())), ',', _trait("Virtual NFT reserves", Strings.toString(privatePool.virtualNftReserves())), ',', _trait("Fee rate (bps): ", Strings.toString(privatePool.feeRate())), ',', _trait("NFT balance", Strings.toString(ERC721(privatePool.nft()).balanceOf(address(privatePool)))), ',', _trait("Base token balance", Strings.toString(privatePool.baseToken() == address(0) ? address(privatePool).balance : ERC20(privatePool.baseToken()).balanceOf(address(privatePool)))) ); return string(_attributes); } /// @notice Returns an svg image for a pool. /// @param tokenId The private pool's token ID. function svg(uint256 tokenId) public view returns (bytes memory) { PrivatePool privatePool = PrivatePool(payable(address(uint160(tokenId)))); // break up svg building into multiple scopes to avoid stack too deep errors bytes memory _svg; { // forgefmt: disable-next-item _svg = abi.encodePacked( '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400" style="width:100%;background:black;fill:white;font-family:serif;">', '<text x="24px" y="24px" font-size="12">', "Caviar AMM private pool position", "</text>", '<text x="24px" y="48px" font-size="12">', "Private pool: ", Strings.toHexString(address(privatePool)), "</text>", '<text x="24px" y="72px" font-size="12">', "Base token: ", Strings.toHexString(privatePool.baseToken()), "</text>", '<text x="24px" y="96px" font-size="12">', "NFT: ", Strings.toHexString(privatePool.nft()), "</text>" ); } { // forgefmt: disable-next-item _svg = abi.encodePacked( _svg, '<text x="24px" y="120px" font-size="12">', "Virtual base token reserves: ", Strings.toString(privatePool.virtualBaseTokenReserves()), "</text>", '<text x="24px" y="144px" font-size="12">', "Virtual NFT reserves: ", Strings.toString(privatePool.virtualNftReserves()), "</text>", '<text x="24px" y="168px" font-size="12">', "Fee rate (bps): ", Strings.toString(privatePool.feeRate()), "</text>" ); } { // forgefmt: disable-next-item _svg = abi.encodePacked( _svg, '<text x="24px" y="192px" font-size="12">', "NFT balance: ", Strings.toString(ERC721(privatePool.nft()).balanceOf(address(privatePool))), "</text>", '<text x="24px" y="216px" font-size="12">', "Base token balance: ", Strings.toString(privatePool.baseToken() == address(0) ? address(privatePool).balance : ERC20(privatePool.baseToken()).balanceOf(address(privatePool))), "</text>", "</svg>" ); } return _svg; } function _trait(string memory traitType, string memory value) internal pure returns (string memory) { // forgefmt: disable-next-item return string( abi.encodePacked( '{ "trait_type": "', traitType, '",', '"value": "', value, '" }' ) ); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ 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); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol) pragma solidity ^0.8.0; /** * @dev Provides a set of functions to operate with Base64 strings. * * _Available since v4.5._ */ library Base64 { /** * @dev Base64 Encoding/Decoding Table */ string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /** * @dev Converts a `bytes` to its Bytes64 `string` representation. */ function encode(bytes memory data) internal pure returns (string memory) { /** * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol */ if (data.length == 0) return ""; // Loads the table into memory string memory table = _TABLE; // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter // and split into 4 numbers of 6 bits. // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up // - `data.length + 2` -> Round up // - `/ 3` -> Number of 3-bytes chunks // - `4 *` -> 4 characters for each chunk string memory result = new string(4 * ((data.length + 2) / 3)); /// @solidity memory-safe-assembly assembly { // Prepare the lookup table (skip the first "length" byte) let tablePtr := add(table, 1) // Prepare result pointer, jump over length let resultPtr := add(result, 32) // Run over the input, 3 bytes at a time for { let dataPtr := data let endPtr := add(data, mload(data)) } lt(dataPtr, endPtr) { } { // Advance 3 bytes dataPtr := add(dataPtr, 3) let input := mload(dataPtr) // To write each character, shift the 3 bytes (18 bits) chunk // 4 times in blocks of 6 bits for each character (18, 12, 6, 0) // and apply logical AND with 0x3F which is the number of // the previous character in the ASCII table prior to the Base64 Table // The result is then added to the table to get the character to write, // and finally write it in the result pointer but with a left shift // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F)))) resultPtr := add(resultPtr, 1) // Advance mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F)))) resultPtr := add(resultPtr, 1) // Advance mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F)))) resultPtr := add(resultPtr, 1) // Advance mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F)))) resultPtr := add(resultPtr, 1) // Advance } // When data `bytes` is not exactly 3 bytes long // it is padded with `=` characters at the end switch mod(mload(data), 3) case 1 { mstore8(sub(resultPtr, 1), 0x3d) mstore8(sub(resultPtr, 2), 0x3d) } case 2 { mstore8(sub(resultPtr, 1), 0x3d) } } return result; } }
{ "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/caviar/lib/ERC721A/contracts/", "caviar/=lib/caviar/src/", "create2-helpers/=lib/royalty-registry-solidity/lib/create2-helpers/", "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-upgradeable/=lib/royalty-registry-solidity/lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "openzeppelin/=lib/openzeppelin-contracts/contracts/", "oracle/=lib/caviar/lib/oracle/contracts/", "reservoir-oracle/=lib/caviar/lib/oracle/contracts/", "royalty-registry-solidity/=lib/royalty-registry-solidity/contracts/", "solady/=lib/solady/src/", "solmate/=lib/solmate/src/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_royaltyRegistry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"DeadlinePassed","type":"error"},{"inputs":[],"name":"InvalidRoyaltyFee","type":"error"},{"inputs":[],"name":"MismatchedTokenIds","type":"error"},{"inputs":[],"name":"OutputAmountTooSmall","type":"error"},{"inputs":[],"name":"PriceOutOfRange","type":"error"},{"inputs":[{"components":[{"internalType":"address payable","name":"pool","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"tokenWeights","type":"uint256[]"},{"components":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"bool[]","name":"flags","type":"bool[]"}],"internalType":"struct PrivatePool.MerkleMultiProof","name":"proof","type":"tuple"},{"internalType":"uint256","name":"baseTokenAmount","type":"uint256"},{"internalType":"bool","name":"isPublicPool","type":"bool"}],"internalType":"struct EthRouter.Buy[]","name":"buys","type":"tuple[]"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"payRoyalties","type":"bool"}],"name":"buy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address payable","name":"pool","type":"address"},{"internalType":"uint256[]","name":"inputTokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"inputTokenWeights","type":"uint256[]"},{"components":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"bool[]","name":"flags","type":"bool[]"}],"internalType":"struct PrivatePool.MerkleMultiProof","name":"inputProof","type":"tuple"},{"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 IStolenNftOracle.Message[]","name":"stolenNftProofs","type":"tuple[]"},{"internalType":"uint256[]","name":"outputTokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"outputTokenWeights","type":"uint256[]"},{"components":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"bool[]","name":"flags","type":"bool[]"}],"internalType":"struct PrivatePool.MerkleMultiProof","name":"outputProof","type":"tuple"},{"internalType":"uint256","name":"baseTokenAmount","type":"uint256"},{"internalType":"bool","name":"isPublicPool","type":"bool"}],"internalType":"struct EthRouter.Change[]","name":"changes","type":"tuple[]"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"change","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address payable","name":"privatePool","type":"address"},{"internalType":"address","name":"nft","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256","name":"minPrice","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nft","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"getRoyalty","outputs":[{"internalType":"uint256","name":"royaltyFee","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"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":[],"name":"royaltyRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address payable","name":"pool","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"tokenWeights","type":"uint256[]"},{"components":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"bool[]","name":"flags","type":"bool[]"}],"internalType":"struct PrivatePool.MerkleMultiProof","name":"proof","type":"tuple"},{"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 IStolenNftOracle.Message[]","name":"stolenNftProofs","type":"tuple[]"},{"internalType":"bool","name":"isPublicPool","type":"bool"},{"internalType":"bytes32[][]","name":"publicPoolProofs","type":"bytes32[][]"}],"internalType":"struct EthRouter.Sell[]","name":"sells","type":"tuple[]"},{"internalType":"uint256","name":"minOutputAmount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"payRoyalties","type":"bool"}],"name":"sell","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60a06040523480156200001157600080fd5b5060405162002b5b38038062002b5b833981016040819052620000349162000046565b6001600160a01b031660805262000078565b6000602082840312156200005957600080fd5b81516001600160a01b03811681146200007157600080fd5b9392505050565b608051612ac06200009b6000396000818161012301526115830152612ac06000f3fe6080604052600436106100735760003560e01c80639aa003aa1161004e5780639aa003aa146100fe578063a11b071214610111578063bd326de11461015d578063f533b8021461017057600080fd5b80629836b31461007f578063150b7a02146100a15780631c73f691146100eb57600080fd5b3661007a57005b600080fd5b34801561008b57600080fd5b5061009f61009a3660046118a4565b6101ad565b005b3480156100ad57600080fd5b506100cd6100bc366004611923565b630a85bd0160e11b95945050505050565b6040516001600160e01b031990911681526020015b60405180910390f35b61009f6100f93660046119c1565b610845565b61009f61010c366004611a1f565b610e05565b34801561011d57600080fd5b506101457f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100e2565b61009f61016b366004611a75565b61138f565b34801561017c57600080fd5b5061019061018b366004611af4565b61155d565b604080519283526001600160a01b039091166020830152016100e2565b81421180156101bb57508115155b156101d95760405163387b2e5560e11b815260040160405180910390fd5b60005b848110156108125760008686838181106101f8576101f8611b29565b905060200281019061020a9190611b3f565b610218906020810190611b5f565b6001600160a01b03166347ccca026040518163ffffffff1660e01b8152600401602060405180830381865afa158015610255573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102799190611b83565b905060005b87878481811061029057610290611b29565b90506020028101906102a29190611b3f565b6102b0906020810190611ba0565b905081101561037657816001600160a01b03166342842e0e33308b8b888181106102dc576102dc611b29565b90506020028101906102ee9190611b3f565b6102fc906020810190611ba0565b8681811061030c5761030c611b29565b905060200201356040518463ffffffff1660e01b815260040161033193929190611be9565b600060405180830381600087803b15801561034b57600080fd5b505af115801561035f573d6000803e3d6000fd5b50505050808061036e90611c23565b91505061027e565b506103b28188888581811061038d5761038d611b29565b905060200281019061039f9190611b3f565b6103ad906020810190611b5f565b611704565b8686838181106103c4576103c4611b29565b90506020028101906103d69190611b3f565b6103e79060c081019060a001611c3c565b1561068e57600087878481811061040057610400611b29565b90506020028101906104129190611b3f565b610420906020810190611b5f565b6001600160a01b0316635accf4a589898681811061044057610440611b29565b90506020028101906104529190611b3f565b610460906020810190611ba0565b6000808d8d8a81811061047557610475611b29565b90506020028101906104879190611b3f565b6104959060c0810190611ba0565b8f8f8c8181106104a7576104a7611b29565b90506020028101906104b99190611b3f565b6104c7906080810190611ba0565b6040516020016104d8929190611d7d565b6040516020818303038152906040528060200190518101906104fa9190611f04565b6040518863ffffffff1660e01b815260040161051c979695949392919061215d565b6020604051808303816000875af115801561053b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061055f91906121fa565b9050831561068857600088888581811061057b5761057b611b29565b905060200281019061058d9190611b3f565b61059b906020810190611ba0565b6105a6915083612213565b905060005b8989868181106105bd576105bd611b29565b90506020028101906105cf9190611b3f565b6105dd906020810190611ba0565b90508110156106855760008061063c868d8d8a8181106105ff576105ff611b29565b90506020028101906106119190611b3f565b61061f906020810190611ba0565b8681811061062f5761062f611b29565b905060200201358661155d565b9150915060008211801561065857506001600160a01b03811615155b15610670576106706001600160a01b038216836117e3565b5050808061067d90611c23565b9150506105ab565b50505b506107ff565b8686838181106106a0576106a0611b29565b90506020028101906106b29190611b3f565b6106c0906020810190611b5f565b6001600160a01b03166394bc65878888858181106106e0576106e0611b29565b90506020028101906106f29190611b3f565b610700906020810190611ba0565b8a8a8781811061071257610712611b29565b90506020028101906107249190611b3f565b610732906040810190611ba0565b8c8c8981811061074457610744611b29565b90506020028101906107569190611b3f565b610764906060810190612235565b8d8d8a81811061077657610776611b29565b90506020028101906107889190611b3f565b610796906080810190611ba0565b6040518863ffffffff1660e01b81526004016107b897969594939291906122c1565b6060604051808303816000875af11580156107d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107fb919061231f565b5050505b508061080a81611c23565b9150506101dc565b5082471015610834576040516317ba44d760e21b815260040160405180910390fd5b61083e33476117e3565b5050505050565b814211801561085357508115155b156108715760405163387b2e5560e11b815260040160405180910390fd5b60005b83811015610dee57600085858381811061089057610890611b29565b90506020028101906108a2919061234d565b6108b0906020810190611b5f565b6001600160a01b03166347ccca026040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109119190611b83565b905085858381811061092557610925611b29565b9050602002810190610937919061234d565b6109489060c081019060a001611c3c565b15610b7957600086868481811061096157610961611b29565b9050602002810190610973919061234d565b610981906020810190611b5f565b6001600160a01b0316632a6f869d8888868181106109a1576109a1611b29565b90506020028101906109b3919061234d565b608001358989878181106109c9576109c9611b29565b90506020028101906109db919061234d565b6109e9906020810190611ba0565b8b8b898181106109fb576109fb611b29565b9050602002810190610a0d919061234d565b6080013560006040518663ffffffff1660e01b8152600401610a329493929190612363565b60206040518083038185885af1158015610a50573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610a7591906121fa565b90508315610b73576000878785818110610a9157610a91611b29565b9050602002810190610aa3919061234d565b610ab1906020810190611ba0565b610abc915083612213565b905060005b888886818110610ad357610ad3611b29565b9050602002810190610ae5919061234d565b610af3906020810190611ba0565b9050811015610b7057600080610b27868c8c8a818110610b1557610b15611b29565b9050602002810190610611919061234d565b91509150600082118015610b4357506001600160a01b03811615155b15610b5b57610b5b6001600160a01b038216836117e3565b50508080610b6890611c23565b915050610ac1565b50505b50610cde565b858583818110610b8b57610b8b611b29565b9050602002810190610b9d919061234d565b610bab906020810190611b5f565b6001600160a01b03166354d63d56878785818110610bcb57610bcb611b29565b9050602002810190610bdd919061234d565b60800135888886818110610bf357610bf3611b29565b9050602002810190610c05919061234d565b610c13906020810190611ba0565b8a8a88818110610c2557610c25611b29565b9050602002810190610c37919061234d565b610c45906040810190611ba0565b8c8c8a818110610c5757610c57611b29565b9050602002810190610c69919061234d565b610c77906060810190612235565b6040518763ffffffff1660e01b8152600401610c9795949392919061238a565b60606040518083038185885af1158015610cb5573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610cda919061231f565b5050505b60005b868684818110610cf357610cf3611b29565b9050602002810190610d05919061234d565b610d13906020810190611ba0565b9050811015610dd957816001600160a01b03166342842e0e30338a8a88818110610d3f57610d3f611b29565b9050602002810190610d51919061234d565b610d5f906020810190611ba0565b86818110610d6f57610d6f611b29565b905060200201356040518463ffffffff1660e01b8152600401610d9493929190611be9565b600060405180830381600087803b158015610dae57600080fd5b505af1158015610dc2573d6000803e3d6000fd5b505050508080610dd190611c23565b915050610ce1565b50508080610de690611c23565b915050610874565b504715610dff57610dff33476117e3565b50505050565b8042118015610e1357508015155b15610e315760405163387b2e5560e11b815260040160405180910390fd5b60005b82811015611379576000848483818110610e5057610e50611b29565b9050602002810190610e6291906123d1565b610e6b906126bc565b9050600081600001516001600160a01b03166347ccca026040518163ffffffff1660e01b8152600401602060405180830381865afa158015610eb1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ed59190611b83565b905060005b868685818110610eec57610eec611b29565b9050602002810190610efe91906123d1565b610f0c906020810190611ba0565b9050811015610fa757816001600160a01b03166342842e0e333086602001518581518110610f3c57610f3c611b29565b60200260200101516040518463ffffffff1660e01b8152600401610f6293929190611be9565b600060405180830381600087803b158015610f7c57600080fd5b505af1158015610f90573d6000803e3d6000fd5b505050508080610f9f90611c23565b915050610eda565b50610fb6818360000151611704565b816101200151156111f0578160a001515182602001515114610feb5760405163a0b9446560e01b815260040160405180910390fd5b6040805160008082526020820190925281611016565b60608152602001906001900390816110015790505b509050600083600001516001600160a01b03166335ec39d985602001518487608001516040516020016110499190612810565b60405160208183030381529060405280602001905181019061106b9190611f04565b6040518463ffffffff1660e01b81526004016110899392919061285e565b6020604051808303816000875af11580156110a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110cc91906121fa565b905060006103e86110de8360036128d5565b6110e89190612213565b855161010087015160405163204c9d9360e11b81526004810184905260248101829052600060448201529293506001600160a01b03909116916340993b26919060640160206040518083038185885af1158015611149573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061116e91906121fa565b50845160a0860151604051632866057160e01b81526001600160a01b03909216916328660571916111a4916001906004016128f2565b6020604051808303816000875af11580156111c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e791906121fa565b50505050611293565b81600001516001600160a01b031663cad175dc83610100015184602001518560400151866060015187608001518860a001518960c001518a60e001516040518963ffffffff1660e01b815260040161124e979695949392919061296e565b604080518083038185885af115801561126b573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061129091906129f7565b50505b60005b8686858181106112a8576112a8611b29565b90506020028101906112ba91906123d1565b6112c89060a0810190611ba0565b905081101561136357816001600160a01b03166342842e0e30338660a0015185815181106112f8576112f8611b29565b60200260200101516040518463ffffffff1660e01b815260040161131e93929190611be9565b600060405180830381600087803b15801561133857600080fd5b505af115801561134c573d6000803e3d6000fd5b50505050808061135b90611c23565b915050611296565b505050808061137190611c23565b915050610e34565b50471561138a5761138a33476117e3565b505050565b804211801561139d57508015155b156113bb5760405163387b2e5560e11b815260040160405180910390fd5b6000876001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061141f91906121fa565b90508281118061142e57508381105b1561144c57604051630df22a0f60e21b815260040160405180910390fd5b60005b858110156114e457876001600160a01b03166342842e0e33308a8a8681811061147a5761147a611b29565b905060200201356040518463ffffffff1660e01b815260040161149f93929190611be9565b600060405180830381600087803b1580156114b957600080fd5b505af11580156114cd573d6000803e3d6000fd5b5050505080806114dc90611c23565b91505061144f565b506114ef8789611704565b604051635dc55f2f60e01b81526001600160a01b03891690635dc55f2f903490611521908a908a908490600401612a1b565b6000604051808303818588803b15801561153a57600080fd5b505af115801561154e573d6000803e3d6000fd5b50505050505050505050505050565b60405163de5488af60e01b81526001600160a01b038481166004830152600091829182917f00000000000000000000000000000000000000000000000000000000000000009091169063de5488af90602401602060405180830381865afa1580156115cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f09190611b83565b6040516301ffc9a760e01b815263152a902d60e11b60048201529091506001600160a01b038216906301ffc9a790602401602060405180830381865afa15801561163e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116629190612a3f565b156116fb5760405163152a902d60e11b815260048101869052602481018590526001600160a01b03821690632a55205a906044016040805180830381865afa1580156116b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116d69190612a5c565b93509150838311156116fb576040516310f4520760e21b815260040160405180910390fd5b50935093915050565b60405163e985e9c560e01b81523060048201526001600160a01b03828116602483015283169063e985e9c590604401602060405180830381865afa158015611750573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117749190612a3f565b1561177d575050565b60405163a22cb46560e01b81526001600160a01b0382811660048301526001602483015283169063a22cb46590604401600060405180830381600087803b1580156117c757600080fd5b505af11580156117db573d6000803e3d6000fd5b505050505050565b600080600080600085875af190508061138a5760405162461bcd60e51b815260206004820152601360248201527211551217d514905394d1915497d19052531151606a1b604482015260640160405180910390fd5b60008083601f84011261184a57600080fd5b5081356001600160401b0381111561186157600080fd5b6020830191508360208260051b850101111561187c57600080fd5b9250929050565b801515811461189157600080fd5b50565b803561189f81611883565b919050565b6000806000806000608086880312156118bc57600080fd5b85356001600160401b038111156118d257600080fd5b6118de88828901611838565b9096509450506020860135925060408601359150606086013561190081611883565b809150509295509295909350565b6001600160a01b038116811461189157600080fd5b60008060008060006080868803121561193b57600080fd5b85356119468161190e565b945060208601356119568161190e565b93506040860135925060608601356001600160401b038082111561197957600080fd5b818801915088601f83011261198d57600080fd5b81358181111561199c57600080fd5b8960208285010111156119ae57600080fd5b9699959850939650602001949392505050565b600080600080606085870312156119d757600080fd5b84356001600160401b038111156119ed57600080fd5b6119f987828801611838565b909550935050602085013591506040850135611a1481611883565b939692955090935050565b600080600060408486031215611a3457600080fd5b83356001600160401b03811115611a4a57600080fd5b611a5686828701611838565b909790965060209590950135949350505050565b803561189f8161190e565b600080600080600080600060c0888a031215611a9057600080fd5b8735611a9b8161190e565b96506020880135611aab8161190e565b955060408801356001600160401b03811115611ac657600080fd5b611ad28a828b01611838565b989b979a50986060810135976080820135975060a09091013595509350505050565b600080600060608486031215611b0957600080fd5b8335611b148161190e565b95602085013595506040909401359392505050565b634e487b7160e01b600052603260045260246000fd5b6000823560de19833603018112611b5557600080fd5b9190910192915050565b600060208284031215611b7157600080fd5b8135611b7c8161190e565b9392505050565b600060208284031215611b9557600080fd5b8151611b7c8161190e565b6000808335601e19843603018112611bb757600080fd5b8301803591506001600160401b03821115611bd157600080fd5b6020019150600581901b360382131561187c57600080fd5b6001600160a01b039384168152919092166020820152604081019190915260600190565b634e487b7160e01b600052601160045260246000fd5b600060018201611c3557611c35611c0d565b5060010190565b600060208284031215611c4e57600080fd5b8135611b7c81611883565b6000808335601e19843603018112611c7057600080fd5b83016020810192503590506001600160401b03811115611c8f57600080fd5b80360382131561187c57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b81835260006020808501808196508560051b81019150846000805b88811015611d6f578385038a528235607e19893603018112611d02578283fd5b8801803586526080611d1688830183611c59565b828a8a0152611d28838a018284611c9e565b9250505060408083013581890152506060611d4581840184611c59565b9350888303828a0152611d59838583611c9e565b9d8a019d98505050938701935050600101611ce2565b509298975050505050505050565b602081526000611d91602083018486611cc7565b949350505050565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b0381118282101715611dd157611dd1611d99565b60405290565b604080519081016001600160401b0381118282101715611dd157611dd1611d99565b60405161014081016001600160401b0381118282101715611dd157611dd1611d99565b604051601f8201601f191681016001600160401b0381118282101715611e4457611e44611d99565b604052919050565b60006001600160401b03821115611e6557611e65611d99565b5060051b60200190565b60006001600160401b03821115611e8857611e88611d99565b50601f01601f191660200190565b60005b83811015611eb1578181015183820152602001611e99565b50506000910152565b600082601f830112611ecb57600080fd5b8151611ede611ed982611e6f565b611e1c565b818152846020838601011115611ef357600080fd5b611d91826020830160208701611e96565b60006020808385031215611f1757600080fd5b82516001600160401b0380821115611f2e57600080fd5b818501915085601f830112611f4257600080fd5b8151611f50611ed982611e4c565b81815260059190911b83018401908481019088831115611f6f57600080fd5b8585015b8381101561201957805185811115611f8b5760008081fd5b86016080818c03601f1901811315611fa35760008081fd5b611fab611daf565b89830151815260408084015189811115611fc55760008081fd5b611fd38f8d83880101611eba565b838d01525060608481015182840152928401519289841115611ff757600091508182fd5b6120058f8d86880101611eba565b908301525085525050918601918601611f73565b5098975050505050505050565b81835260006001600160fb1b0383111561203f57600080fd5b8260051b80836020870137939093016020019392505050565b6000808335601e1984360301811261206f57600080fd5b83016020810192503590506001600160401b0381111561208e57600080fd5b8060051b360382131561187c57600080fd5b600081518084526120b8816020860160208601611e96565b601f01601f19169290920160200192915050565b600081518084526020808501808196508360051b8101915082860160005b858110156121505782840389528151608081518652868201518188880152612114828801826120a0565b91505060408083015181880152506060808301519250868203818801525061213c81836120a0565b9a87019a95505050908401906001016120ea565b5091979650505050505050565b60a08152600061217160a08301898b612026565b602088818501528760408501528382036060850152818683528183019050818760051b8401018860005b898110156121d557858303601f190184526121b6828c612058565b6121c1858284612026565b95870195945050509084019060010161219b565b505085810360808701526121e981886120cc565b9d9c50505050505050505050505050565b60006020828403121561220c57600080fd5b5051919050565b60008261223057634e487b7160e01b600052601260045260246000fd5b500490565b60008235603e19833603018112611b5557600080fd5b60006122578283612058565b60408552612269604086018284612026565b915050602061227a81850185612058565b8684038388015280845290926000919083015b818310156122b65784356122a081611883565b151581529383019360019290920191830161228d565b979650505050505050565b6080815260006122d560808301898b612026565b82810360208401526122e881888a612026565b905082810360408401526122fc818761224b565b90508281036060840152612311818587611cc7565b9a9950505050505050505050565b60008060006060848603121561233457600080fd5b8351925060208401519150604084015190509250925092565b6000823560be19833603018112611b5557600080fd5b606081526000612377606083018688612026565b6020830194909452506040015292915050565b60608152600061239e606083018789612026565b82810360208401526123b1818688612026565b905082810360408401526123c5818561224b565b98975050505050505050565b6000823561013e19833603018112611b5557600080fd5b600082601f8301126123f957600080fd5b81356020612409611ed983611e4c565b82815260059290921b8401810191818101908684111561242857600080fd5b8286015b84811015612443578035835291830191830161242c565b509695505050505050565b600082601f83011261245f57600080fd5b8135602061246f611ed983611e4c565b82815260059290921b8401810191818101908684111561248e57600080fd5b8286015b848110156124435780356124a581611883565b8352918301918301612492565b6000604082840312156124c457600080fd5b6124cc611dd7565b905081356001600160401b03808211156124e557600080fd5b818401915084601f8301126124f957600080fd5b81356020612509611ed983611e4c565b82815260059290921b8401810191818101908884111561252857600080fd5b948201945b838610156125465785358252948201949082019061252d565b8652508581013593508284111561255c57600080fd5b6125688785880161244e565b818601525050505092915050565b600082601f83011261258757600080fd5b8135612595611ed982611e6f565b8181528460208386010111156125aa57600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f8301126125d857600080fd5b813560206125e8611ed983611e4c565b82815260059290921b8401810191818101908684111561260757600080fd5b8286015b848110156124435780356001600160401b038082111561262b5760008081fd5b908801906080828b03601f19018113156126455760008081fd5b61264d611daf565b878401358152604080850135848111156126675760008081fd5b6126758e8b83890101612576565b838b0152506060858101358284015292850135928484111561269957600091508182fd5b6126a78e8b86890101612576565b9083015250865250505091830191830161260b565b600061014082360312156126cf57600080fd5b6126d7611df9565b6126e083611a6a565b815260208301356001600160401b03808211156126fc57600080fd5b612708368387016123e8565b6020840152604085013591508082111561272157600080fd5b61272d368387016123e8565b6040840152606085013591508082111561274657600080fd5b612752368387016124b2565b6060840152608085013591508082111561276b57600080fd5b612777368387016125c7565b608084015260a085013591508082111561279057600080fd5b61279c368387016123e8565b60a084015260c08501359150808211156127b557600080fd5b6127c1368387016123e8565b60c084015260e08501359150808211156127da57600080fd5b506127e7368286016124b2565b60e0830152506101008381013590820152610120612806818501611894565b9082015292915050565b602081526000611b7c60208301846120cc565b600081518084526020808501945080840160005b8381101561285357815187529582019590820190600101612837565b509495945050505050565b6060815260006128716060830186612823565b6020838203818501528186518084528284019150828160051b85010183890160005b838110156128c157601f198784030185526128af838351612823565b94860194925090850190600101612893565b5050868103604088015261231181896120cc565b80820281158282048414176128ec576128ec611c0d565b92915050565b6040815260006129056040830185612823565b905082151560208301529392505050565b600081516040845261292b6040850182612823565b60208481015186830387830152805180845290820193509091600091908301905b808310156124435784511515825293830193600192909201919083019061294c565b60e08152600061298160e083018a612823565b8281036020840152612993818a612823565b905082810360408401526129a78189612916565b905082810360608401526129bb81886120cc565b905082810360808401526129cf8187612823565b905082810360a08401526129e38186612823565b905082810360c08401526123118185612916565b60008060408385031215612a0a57600080fd5b505080516020909101519092909150565b604081526000612a2f604083018587612026565b9050826020830152949350505050565b600060208284031215612a5157600080fd5b8151611b7c81611883565b60008060408385031215612a6f57600080fd5b8251612a7a8161190e565b602093909301519294929350505056fea2646970667358221220ecb7e1d1b75f0921c4d10408e15ccba314cc01916330b10b976b7bf274d7c49e64736f6c63430008130033000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d
Deployed Bytecode
0x6080604052600436106100735760003560e01c80639aa003aa1161004e5780639aa003aa146100fe578063a11b071214610111578063bd326de11461015d578063f533b8021461017057600080fd5b80629836b31461007f578063150b7a02146100a15780631c73f691146100eb57600080fd5b3661007a57005b600080fd5b34801561008b57600080fd5b5061009f61009a3660046118a4565b6101ad565b005b3480156100ad57600080fd5b506100cd6100bc366004611923565b630a85bd0160e11b95945050505050565b6040516001600160e01b031990911681526020015b60405180910390f35b61009f6100f93660046119c1565b610845565b61009f61010c366004611a1f565b610e05565b34801561011d57600080fd5b506101457f000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d81565b6040516001600160a01b0390911681526020016100e2565b61009f61016b366004611a75565b61138f565b34801561017c57600080fd5b5061019061018b366004611af4565b61155d565b604080519283526001600160a01b039091166020830152016100e2565b81421180156101bb57508115155b156101d95760405163387b2e5560e11b815260040160405180910390fd5b60005b848110156108125760008686838181106101f8576101f8611b29565b905060200281019061020a9190611b3f565b610218906020810190611b5f565b6001600160a01b03166347ccca026040518163ffffffff1660e01b8152600401602060405180830381865afa158015610255573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102799190611b83565b905060005b87878481811061029057610290611b29565b90506020028101906102a29190611b3f565b6102b0906020810190611ba0565b905081101561037657816001600160a01b03166342842e0e33308b8b888181106102dc576102dc611b29565b90506020028101906102ee9190611b3f565b6102fc906020810190611ba0565b8681811061030c5761030c611b29565b905060200201356040518463ffffffff1660e01b815260040161033193929190611be9565b600060405180830381600087803b15801561034b57600080fd5b505af115801561035f573d6000803e3d6000fd5b50505050808061036e90611c23565b91505061027e565b506103b28188888581811061038d5761038d611b29565b905060200281019061039f9190611b3f565b6103ad906020810190611b5f565b611704565b8686838181106103c4576103c4611b29565b90506020028101906103d69190611b3f565b6103e79060c081019060a001611c3c565b1561068e57600087878481811061040057610400611b29565b90506020028101906104129190611b3f565b610420906020810190611b5f565b6001600160a01b0316635accf4a589898681811061044057610440611b29565b90506020028101906104529190611b3f565b610460906020810190611ba0565b6000808d8d8a81811061047557610475611b29565b90506020028101906104879190611b3f565b6104959060c0810190611ba0565b8f8f8c8181106104a7576104a7611b29565b90506020028101906104b99190611b3f565b6104c7906080810190611ba0565b6040516020016104d8929190611d7d565b6040516020818303038152906040528060200190518101906104fa9190611f04565b6040518863ffffffff1660e01b815260040161051c979695949392919061215d565b6020604051808303816000875af115801561053b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061055f91906121fa565b9050831561068857600088888581811061057b5761057b611b29565b905060200281019061058d9190611b3f565b61059b906020810190611ba0565b6105a6915083612213565b905060005b8989868181106105bd576105bd611b29565b90506020028101906105cf9190611b3f565b6105dd906020810190611ba0565b90508110156106855760008061063c868d8d8a8181106105ff576105ff611b29565b90506020028101906106119190611b3f565b61061f906020810190611ba0565b8681811061062f5761062f611b29565b905060200201358661155d565b9150915060008211801561065857506001600160a01b03811615155b15610670576106706001600160a01b038216836117e3565b5050808061067d90611c23565b9150506105ab565b50505b506107ff565b8686838181106106a0576106a0611b29565b90506020028101906106b29190611b3f565b6106c0906020810190611b5f565b6001600160a01b03166394bc65878888858181106106e0576106e0611b29565b90506020028101906106f29190611b3f565b610700906020810190611ba0565b8a8a8781811061071257610712611b29565b90506020028101906107249190611b3f565b610732906040810190611ba0565b8c8c8981811061074457610744611b29565b90506020028101906107569190611b3f565b610764906060810190612235565b8d8d8a81811061077657610776611b29565b90506020028101906107889190611b3f565b610796906080810190611ba0565b6040518863ffffffff1660e01b81526004016107b897969594939291906122c1565b6060604051808303816000875af11580156107d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107fb919061231f565b5050505b508061080a81611c23565b9150506101dc565b5082471015610834576040516317ba44d760e21b815260040160405180910390fd5b61083e33476117e3565b5050505050565b814211801561085357508115155b156108715760405163387b2e5560e11b815260040160405180910390fd5b60005b83811015610dee57600085858381811061089057610890611b29565b90506020028101906108a2919061234d565b6108b0906020810190611b5f565b6001600160a01b03166347ccca026040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109119190611b83565b905085858381811061092557610925611b29565b9050602002810190610937919061234d565b6109489060c081019060a001611c3c565b15610b7957600086868481811061096157610961611b29565b9050602002810190610973919061234d565b610981906020810190611b5f565b6001600160a01b0316632a6f869d8888868181106109a1576109a1611b29565b90506020028101906109b3919061234d565b608001358989878181106109c9576109c9611b29565b90506020028101906109db919061234d565b6109e9906020810190611ba0565b8b8b898181106109fb576109fb611b29565b9050602002810190610a0d919061234d565b6080013560006040518663ffffffff1660e01b8152600401610a329493929190612363565b60206040518083038185885af1158015610a50573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610a7591906121fa565b90508315610b73576000878785818110610a9157610a91611b29565b9050602002810190610aa3919061234d565b610ab1906020810190611ba0565b610abc915083612213565b905060005b888886818110610ad357610ad3611b29565b9050602002810190610ae5919061234d565b610af3906020810190611ba0565b9050811015610b7057600080610b27868c8c8a818110610b1557610b15611b29565b9050602002810190610611919061234d565b91509150600082118015610b4357506001600160a01b03811615155b15610b5b57610b5b6001600160a01b038216836117e3565b50508080610b6890611c23565b915050610ac1565b50505b50610cde565b858583818110610b8b57610b8b611b29565b9050602002810190610b9d919061234d565b610bab906020810190611b5f565b6001600160a01b03166354d63d56878785818110610bcb57610bcb611b29565b9050602002810190610bdd919061234d565b60800135888886818110610bf357610bf3611b29565b9050602002810190610c05919061234d565b610c13906020810190611ba0565b8a8a88818110610c2557610c25611b29565b9050602002810190610c37919061234d565b610c45906040810190611ba0565b8c8c8a818110610c5757610c57611b29565b9050602002810190610c69919061234d565b610c77906060810190612235565b6040518763ffffffff1660e01b8152600401610c9795949392919061238a565b60606040518083038185885af1158015610cb5573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610cda919061231f565b5050505b60005b868684818110610cf357610cf3611b29565b9050602002810190610d05919061234d565b610d13906020810190611ba0565b9050811015610dd957816001600160a01b03166342842e0e30338a8a88818110610d3f57610d3f611b29565b9050602002810190610d51919061234d565b610d5f906020810190611ba0565b86818110610d6f57610d6f611b29565b905060200201356040518463ffffffff1660e01b8152600401610d9493929190611be9565b600060405180830381600087803b158015610dae57600080fd5b505af1158015610dc2573d6000803e3d6000fd5b505050508080610dd190611c23565b915050610ce1565b50508080610de690611c23565b915050610874565b504715610dff57610dff33476117e3565b50505050565b8042118015610e1357508015155b15610e315760405163387b2e5560e11b815260040160405180910390fd5b60005b82811015611379576000848483818110610e5057610e50611b29565b9050602002810190610e6291906123d1565b610e6b906126bc565b9050600081600001516001600160a01b03166347ccca026040518163ffffffff1660e01b8152600401602060405180830381865afa158015610eb1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ed59190611b83565b905060005b868685818110610eec57610eec611b29565b9050602002810190610efe91906123d1565b610f0c906020810190611ba0565b9050811015610fa757816001600160a01b03166342842e0e333086602001518581518110610f3c57610f3c611b29565b60200260200101516040518463ffffffff1660e01b8152600401610f6293929190611be9565b600060405180830381600087803b158015610f7c57600080fd5b505af1158015610f90573d6000803e3d6000fd5b505050508080610f9f90611c23565b915050610eda565b50610fb6818360000151611704565b816101200151156111f0578160a001515182602001515114610feb5760405163a0b9446560e01b815260040160405180910390fd5b6040805160008082526020820190925281611016565b60608152602001906001900390816110015790505b509050600083600001516001600160a01b03166335ec39d985602001518487608001516040516020016110499190612810565b60405160208183030381529060405280602001905181019061106b9190611f04565b6040518463ffffffff1660e01b81526004016110899392919061285e565b6020604051808303816000875af11580156110a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110cc91906121fa565b905060006103e86110de8360036128d5565b6110e89190612213565b855161010087015160405163204c9d9360e11b81526004810184905260248101829052600060448201529293506001600160a01b03909116916340993b26919060640160206040518083038185885af1158015611149573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061116e91906121fa565b50845160a0860151604051632866057160e01b81526001600160a01b03909216916328660571916111a4916001906004016128f2565b6020604051808303816000875af11580156111c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e791906121fa565b50505050611293565b81600001516001600160a01b031663cad175dc83610100015184602001518560400151866060015187608001518860a001518960c001518a60e001516040518963ffffffff1660e01b815260040161124e979695949392919061296e565b604080518083038185885af115801561126b573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061129091906129f7565b50505b60005b8686858181106112a8576112a8611b29565b90506020028101906112ba91906123d1565b6112c89060a0810190611ba0565b905081101561136357816001600160a01b03166342842e0e30338660a0015185815181106112f8576112f8611b29565b60200260200101516040518463ffffffff1660e01b815260040161131e93929190611be9565b600060405180830381600087803b15801561133857600080fd5b505af115801561134c573d6000803e3d6000fd5b50505050808061135b90611c23565b915050611296565b505050808061137190611c23565b915050610e34565b50471561138a5761138a33476117e3565b505050565b804211801561139d57508015155b156113bb5760405163387b2e5560e11b815260040160405180910390fd5b6000876001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061141f91906121fa565b90508281118061142e57508381105b1561144c57604051630df22a0f60e21b815260040160405180910390fd5b60005b858110156114e457876001600160a01b03166342842e0e33308a8a8681811061147a5761147a611b29565b905060200201356040518463ffffffff1660e01b815260040161149f93929190611be9565b600060405180830381600087803b1580156114b957600080fd5b505af11580156114cd573d6000803e3d6000fd5b5050505080806114dc90611c23565b91505061144f565b506114ef8789611704565b604051635dc55f2f60e01b81526001600160a01b03891690635dc55f2f903490611521908a908a908490600401612a1b565b6000604051808303818588803b15801561153a57600080fd5b505af115801561154e573d6000803e3d6000fd5b50505050505050505050505050565b60405163de5488af60e01b81526001600160a01b038481166004830152600091829182917f000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d9091169063de5488af90602401602060405180830381865afa1580156115cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f09190611b83565b6040516301ffc9a760e01b815263152a902d60e11b60048201529091506001600160a01b038216906301ffc9a790602401602060405180830381865afa15801561163e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116629190612a3f565b156116fb5760405163152a902d60e11b815260048101869052602481018590526001600160a01b03821690632a55205a906044016040805180830381865afa1580156116b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116d69190612a5c565b93509150838311156116fb576040516310f4520760e21b815260040160405180910390fd5b50935093915050565b60405163e985e9c560e01b81523060048201526001600160a01b03828116602483015283169063e985e9c590604401602060405180830381865afa158015611750573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117749190612a3f565b1561177d575050565b60405163a22cb46560e01b81526001600160a01b0382811660048301526001602483015283169063a22cb46590604401600060405180830381600087803b1580156117c757600080fd5b505af11580156117db573d6000803e3d6000fd5b505050505050565b600080600080600085875af190508061138a5760405162461bcd60e51b815260206004820152601360248201527211551217d514905394d1915497d19052531151606a1b604482015260640160405180910390fd5b60008083601f84011261184a57600080fd5b5081356001600160401b0381111561186157600080fd5b6020830191508360208260051b850101111561187c57600080fd5b9250929050565b801515811461189157600080fd5b50565b803561189f81611883565b919050565b6000806000806000608086880312156118bc57600080fd5b85356001600160401b038111156118d257600080fd5b6118de88828901611838565b9096509450506020860135925060408601359150606086013561190081611883565b809150509295509295909350565b6001600160a01b038116811461189157600080fd5b60008060008060006080868803121561193b57600080fd5b85356119468161190e565b945060208601356119568161190e565b93506040860135925060608601356001600160401b038082111561197957600080fd5b818801915088601f83011261198d57600080fd5b81358181111561199c57600080fd5b8960208285010111156119ae57600080fd5b9699959850939650602001949392505050565b600080600080606085870312156119d757600080fd5b84356001600160401b038111156119ed57600080fd5b6119f987828801611838565b909550935050602085013591506040850135611a1481611883565b939692955090935050565b600080600060408486031215611a3457600080fd5b83356001600160401b03811115611a4a57600080fd5b611a5686828701611838565b909790965060209590950135949350505050565b803561189f8161190e565b600080600080600080600060c0888a031215611a9057600080fd5b8735611a9b8161190e565b96506020880135611aab8161190e565b955060408801356001600160401b03811115611ac657600080fd5b611ad28a828b01611838565b989b979a50986060810135976080820135975060a09091013595509350505050565b600080600060608486031215611b0957600080fd5b8335611b148161190e565b95602085013595506040909401359392505050565b634e487b7160e01b600052603260045260246000fd5b6000823560de19833603018112611b5557600080fd5b9190910192915050565b600060208284031215611b7157600080fd5b8135611b7c8161190e565b9392505050565b600060208284031215611b9557600080fd5b8151611b7c8161190e565b6000808335601e19843603018112611bb757600080fd5b8301803591506001600160401b03821115611bd157600080fd5b6020019150600581901b360382131561187c57600080fd5b6001600160a01b039384168152919092166020820152604081019190915260600190565b634e487b7160e01b600052601160045260246000fd5b600060018201611c3557611c35611c0d565b5060010190565b600060208284031215611c4e57600080fd5b8135611b7c81611883565b6000808335601e19843603018112611c7057600080fd5b83016020810192503590506001600160401b03811115611c8f57600080fd5b80360382131561187c57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b81835260006020808501808196508560051b81019150846000805b88811015611d6f578385038a528235607e19893603018112611d02578283fd5b8801803586526080611d1688830183611c59565b828a8a0152611d28838a018284611c9e565b9250505060408083013581890152506060611d4581840184611c59565b9350888303828a0152611d59838583611c9e565b9d8a019d98505050938701935050600101611ce2565b509298975050505050505050565b602081526000611d91602083018486611cc7565b949350505050565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b0381118282101715611dd157611dd1611d99565b60405290565b604080519081016001600160401b0381118282101715611dd157611dd1611d99565b60405161014081016001600160401b0381118282101715611dd157611dd1611d99565b604051601f8201601f191681016001600160401b0381118282101715611e4457611e44611d99565b604052919050565b60006001600160401b03821115611e6557611e65611d99565b5060051b60200190565b60006001600160401b03821115611e8857611e88611d99565b50601f01601f191660200190565b60005b83811015611eb1578181015183820152602001611e99565b50506000910152565b600082601f830112611ecb57600080fd5b8151611ede611ed982611e6f565b611e1c565b818152846020838601011115611ef357600080fd5b611d91826020830160208701611e96565b60006020808385031215611f1757600080fd5b82516001600160401b0380821115611f2e57600080fd5b818501915085601f830112611f4257600080fd5b8151611f50611ed982611e4c565b81815260059190911b83018401908481019088831115611f6f57600080fd5b8585015b8381101561201957805185811115611f8b5760008081fd5b86016080818c03601f1901811315611fa35760008081fd5b611fab611daf565b89830151815260408084015189811115611fc55760008081fd5b611fd38f8d83880101611eba565b838d01525060608481015182840152928401519289841115611ff757600091508182fd5b6120058f8d86880101611eba565b908301525085525050918601918601611f73565b5098975050505050505050565b81835260006001600160fb1b0383111561203f57600080fd5b8260051b80836020870137939093016020019392505050565b6000808335601e1984360301811261206f57600080fd5b83016020810192503590506001600160401b0381111561208e57600080fd5b8060051b360382131561187c57600080fd5b600081518084526120b8816020860160208601611e96565b601f01601f19169290920160200192915050565b600081518084526020808501808196508360051b8101915082860160005b858110156121505782840389528151608081518652868201518188880152612114828801826120a0565b91505060408083015181880152506060808301519250868203818801525061213c81836120a0565b9a87019a95505050908401906001016120ea565b5091979650505050505050565b60a08152600061217160a08301898b612026565b602088818501528760408501528382036060850152818683528183019050818760051b8401018860005b898110156121d557858303601f190184526121b6828c612058565b6121c1858284612026565b95870195945050509084019060010161219b565b505085810360808701526121e981886120cc565b9d9c50505050505050505050505050565b60006020828403121561220c57600080fd5b5051919050565b60008261223057634e487b7160e01b600052601260045260246000fd5b500490565b60008235603e19833603018112611b5557600080fd5b60006122578283612058565b60408552612269604086018284612026565b915050602061227a81850185612058565b8684038388015280845290926000919083015b818310156122b65784356122a081611883565b151581529383019360019290920191830161228d565b979650505050505050565b6080815260006122d560808301898b612026565b82810360208401526122e881888a612026565b905082810360408401526122fc818761224b565b90508281036060840152612311818587611cc7565b9a9950505050505050505050565b60008060006060848603121561233457600080fd5b8351925060208401519150604084015190509250925092565b6000823560be19833603018112611b5557600080fd5b606081526000612377606083018688612026565b6020830194909452506040015292915050565b60608152600061239e606083018789612026565b82810360208401526123b1818688612026565b905082810360408401526123c5818561224b565b98975050505050505050565b6000823561013e19833603018112611b5557600080fd5b600082601f8301126123f957600080fd5b81356020612409611ed983611e4c565b82815260059290921b8401810191818101908684111561242857600080fd5b8286015b84811015612443578035835291830191830161242c565b509695505050505050565b600082601f83011261245f57600080fd5b8135602061246f611ed983611e4c565b82815260059290921b8401810191818101908684111561248e57600080fd5b8286015b848110156124435780356124a581611883565b8352918301918301612492565b6000604082840312156124c457600080fd5b6124cc611dd7565b905081356001600160401b03808211156124e557600080fd5b818401915084601f8301126124f957600080fd5b81356020612509611ed983611e4c565b82815260059290921b8401810191818101908884111561252857600080fd5b948201945b838610156125465785358252948201949082019061252d565b8652508581013593508284111561255c57600080fd5b6125688785880161244e565b818601525050505092915050565b600082601f83011261258757600080fd5b8135612595611ed982611e6f565b8181528460208386010111156125aa57600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f8301126125d857600080fd5b813560206125e8611ed983611e4c565b82815260059290921b8401810191818101908684111561260757600080fd5b8286015b848110156124435780356001600160401b038082111561262b5760008081fd5b908801906080828b03601f19018113156126455760008081fd5b61264d611daf565b878401358152604080850135848111156126675760008081fd5b6126758e8b83890101612576565b838b0152506060858101358284015292850135928484111561269957600091508182fd5b6126a78e8b86890101612576565b9083015250865250505091830191830161260b565b600061014082360312156126cf57600080fd5b6126d7611df9565b6126e083611a6a565b815260208301356001600160401b03808211156126fc57600080fd5b612708368387016123e8565b6020840152604085013591508082111561272157600080fd5b61272d368387016123e8565b6040840152606085013591508082111561274657600080fd5b612752368387016124b2565b6060840152608085013591508082111561276b57600080fd5b612777368387016125c7565b608084015260a085013591508082111561279057600080fd5b61279c368387016123e8565b60a084015260c08501359150808211156127b557600080fd5b6127c1368387016123e8565b60c084015260e08501359150808211156127da57600080fd5b506127e7368286016124b2565b60e0830152506101008381013590820152610120612806818501611894565b9082015292915050565b602081526000611b7c60208301846120cc565b600081518084526020808501945080840160005b8381101561285357815187529582019590820190600101612837565b509495945050505050565b6060815260006128716060830186612823565b6020838203818501528186518084528284019150828160051b85010183890160005b838110156128c157601f198784030185526128af838351612823565b94860194925090850190600101612893565b5050868103604088015261231181896120cc565b80820281158282048414176128ec576128ec611c0d565b92915050565b6040815260006129056040830185612823565b905082151560208301529392505050565b600081516040845261292b6040850182612823565b60208481015186830387830152805180845290820193509091600091908301905b808310156124435784511515825293830193600192909201919083019061294c565b60e08152600061298160e083018a612823565b8281036020840152612993818a612823565b905082810360408401526129a78189612916565b905082810360608401526129bb81886120cc565b905082810360808401526129cf8187612823565b905082810360a08401526129e38186612823565b905082810360c08401526123118185612916565b60008060408385031215612a0a57600080fd5b505080516020909101519092909150565b604081526000612a2f604083018587612026565b9050826020830152949350505050565b600060208284031215612a5157600080fd5b8151611b7c81611883565b60008060408385031215612a6f57600080fd5b8251612a7a8161190e565b602093909301519294929350505056fea2646970667358221220ecb7e1d1b75f0921c4d10408e15ccba314cc01916330b10b976b7bf274d7c49e64736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d
-----Decoded View---------------
Arg [0] : _royaltyRegistry (address): 0xaD2184FB5DBcfC05d8f056542fB25b04fa32A95D
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.