// SPDX-License-Identifier: MITpragmasolidity ^0.8.17;/**
* @title DataTypes
* @author Tomo Protocol
*
* @notice A standard library of data types used throughout the XRGB.
*/libraryDataTypes{
structCreateTomojiParameters {
address creator;
uint256 nftTotalSupply;
uint256 reserved;
uint256 maxPerWallet;
uint256 price;
uint256 preSaleDeadLine;
uint160 sqrtPriceX96;
uint160 sqrtPriceB96;
bool bSupportEOAMint;
string name;
string symbol;
string baseURI;
string contractURI;
}
structSwapRouter {
address routerAddr;
address uniswapV3NonfungiblePositionManager;
}
}
Contract Source Code
File 2 of 15: DoubleEndedQueue.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/DoubleEndedQueue.sol)// Modified by Pandora Labs to support native uint256 operationspragmasolidity ^0.8.20;/**
* @dev A sequence of items with the ability to efficiently push and pop items (i.e. insert and remove) on both ends of
* the sequence (called front and back). Among other access patterns, it can be used to implement efficient LIFO and
* FIFO queues. Storage use is optimized, and all operations are O(1) constant time. This includes {clear}, given that
* the existing queue contents are left in storage.
*
* The struct is called `Uint256Deque`. This data structure can only be used in storage, and not in memory.
*
* ```solidity
* DoubleEndedQueue.Uint256Deque queue;
* ```
*/libraryDoubleEndedQueue{
/**
* @dev An operation (e.g. {front}) couldn't be completed due to the queue being empty.
*/errorQueueEmpty();
/**
* @dev A push operation couldn't be completed due to the queue being full.
*/errorQueueFull();
/**
* @dev An operation (e.g. {at}) couldn't be completed due to an index being out of bounds.
*/errorQueueOutOfBounds();
/**
* @dev Indices are 128 bits so begin and end are packed in a single storage slot for efficient access.
*
* Struct members have an underscore prefix indicating that they are "private" and should not be read or written to
* directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and
* lead to unexpected behavior.
*
* The first item is at data[begin] and the last item is at data[end - 1]. This range can wrap around.
*/structUint256Deque {
uint128 _begin;
uint128 _end;
mapping(uint128 index =>uint256) _data;
}
/**
* @dev Removes the item at the end of the queue and returns it.
*
* Reverts with {QueueEmpty} if the queue is empty.
*/functionpopBack(
Uint256Deque storage deque
) internalreturns (uint256 value) {
unchecked {
uint128 backIndex = deque._end;
if (backIndex == deque._begin) revert QueueEmpty();
--backIndex;
value = deque._data[backIndex];
delete deque._data[backIndex];
deque._end = backIndex;
}
}
/**
* @dev Inserts an item at the beginning of the queue.
*
* Reverts with {QueueFull} if the queue is full.
*/functionpushFront(Uint256Deque storage deque, uint256 value) internal{
unchecked {
uint128 frontIndex = deque._begin -1;
if (frontIndex == deque._end) revert QueueFull();
deque._data[frontIndex] = value;
deque._begin = frontIndex;
}
}
/**
* @dev Return the item at a position in the queue given by `index`, with the first item at 0 and last item at
* `length(deque) - 1`.
*
* Reverts with `QueueOutOfBounds` if the index is out of bounds.
*/functionat(
Uint256Deque storage deque,
uint256 index
) internalviewreturns (uint256 value) {
if (index >= length(deque)) revert QueueOutOfBounds();
// By construction, length is a uint128, so the check above ensures that index can be safely downcast to uint128unchecked {
return deque._data[deque._begin +uint128(index)];
}
}
/**
* @dev Returns the number of items in the queue.
*/functionlength(
Uint256Deque storage deque
) internalviewreturns (uint256) {
unchecked {
returnuint256(deque._end - deque._begin);
}
}
/**
* @dev Returns true if the queue is empty.
*/functionempty(Uint256Deque storage deque) internalviewreturns (bool) {
return deque._end == deque._begin;
}
}
Contract Source Code
File 3 of 15: ERC20Events.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.20;libraryERC20Events{
eventApproval(addressindexed owner,
addressindexed spender,
uint256 value
);
eventTransfer(addressindexedfrom, addressindexed to, uint256 amount);
}
Contract Source Code
File 4 of 15: ERC404.sol
//SPDX-License-Identifier: MITpragmasolidity ^0.8.20;import {IERC721Receiver} from"@openzeppelin/contracts/interfaces/IERC721Receiver.sol";
import {IERC165} from"@openzeppelin/contracts/interfaces/IERC165.sol";
import {IERC404} from"./interfaces/IERC404.sol";
import {ERC721Events} from"./libraries/ERC721Events.sol";
import {ERC20Events} from"./libraries/ERC20Events.sol";
import {DoubleEndedQueue} from"./libraries/DoubleEndedQueue.sol";
abstractcontractERC404isIERC404{
usingDoubleEndedQueueforDoubleEndedQueue.Uint256Deque;
/// @dev The queue of ERC-721 tokens stored in the contract.
DoubleEndedQueue.Uint256Deque private _storedERC721Ids;
/// @dev Token namestringpublic name;
/// @dev Token symbolstringpublic symbol;
/// @dev Decimals for ERC-20 representationuint8public decimals;
/// @dev Units for ERC-20 representationuint256internal units;
/// @dev Total supply in ERC-20 representationuint256public totalSupply;
/// @dev Current mint counter which also represents the highest/// minted id, monotonically increasing to ensure accurate ownershipuint256private minted;
/// @dev Balance of user in ERC-20 representationmapping(address=>uint256) public balanceOf;
/// @dev Allowance of user in ERC-20 representationmapping(address=>mapping(address=>uint256)) public allowance;
/// @dev Approval in ERC-721 representaionmapping(uint256=>address) public getApproved;
/// @dev Approval for all in ERC-721 representationmapping(address=>mapping(address=>bool)) public isApprovedForAll;
/// @dev Packed representation of ownerOf and owned indicesmapping(uint256=>uint256) internal _ownedData;
/// @dev Array of owned ids in ERC-721 representationmapping(address=>uint256[]) internal _owned;
/// @dev Addresses that are exempt from ERC-721 transfer, typically for gas savings (pairs, routers, etc)mapping(address=>bool) internal _erc721TransferExempt;
/// @dev Address bitmask for packed ownership datauint256privateconstant _BITMASK_ADDRESS = (1<<160) -1;
/// @dev Owned index bitmask for packed ownership datauint256privateconstant _BITMASK_OWNED_INDEX = ((1<<96) -1) <<160;
/// @notice Function to find owner of a given ERC-721 tokenfunctionownerOf(uint256 id_
) publicviewvirtualreturns (address erc721Owner) {
erc721Owner = _getOwnerOf(id_);
if (!_isValidTokenId(id_)) {
revert InvalidTokenId();
}
if (erc721Owner ==address(0)) {
revert NotFound();
}
}
// function owned(// address owner_// ) public view virtual returns (uint256[] memory) {// return _owned[owner_];// }functionerc721BalanceOf(address owner_
) publicviewvirtualreturns (uint256) {
return _owned[owner_].length;
}
functionerc20BalanceOf(address owner_
) publicviewvirtualreturns (uint256) {
return balanceOf[owner_];
}
// function erc20TotalSupply() public view virtual returns (uint256) {// return totalSupply;// }// function erc721TotalSupply() public view virtual returns (uint256) {// return minted - _storedERC721Ids.length();// }// function getERC721TokensInQueue(// uint256 start_,// uint256 count_// ) public view virtual returns (uint256[] memory) {// uint256[] memory tokensInQueue = new uint256[](count_);// for (uint256 i = start_; i < start_ + count_; ) {// tokensInQueue[i - start_] = _storedERC721Ids.at(i);// unchecked {// ++i;// }// }// return tokensInQueue;// }/// @notice tokenURI must be implemented by child contractfunctiontokenURI(uint256 id_) publicviewvirtualreturns (stringmemory);
/// @notice Function for token approvals/// @dev This function assumes the operator is attempting to approve/// an ERC-721 if valueOrId_ is a possibly valid ERC-721 token id./// Unlike setApprovalForAll, spender_ must be allowed to be 0x0 so/// that approval can be revoked.functionapprove(address spender_,
uint256 valueOrId_
) publicvirtualreturns (bool) {
if (_isValidTokenId(valueOrId_)) {
erc721Approve(spender_, valueOrId_);
} else {
return erc20Approve(spender_, valueOrId_);
}
returntrue;
}
functionerc721Approve(address spender_, uint256 id_) publicvirtual{
// Intention is to approve as ERC-721 token (id).address erc721Owner = _getOwnerOf(id_);
if (
msg.sender!= erc721Owner &&!isApprovedForAll[erc721Owner][msg.sender]
) {
revert Unauthorized();
}
getApproved[id_] = spender_;
emit ERC721Events.Approval(erc721Owner, spender_, id_);
}
/// @dev Providing type(uint256).max for approval value results in an/// unlimited approval that is not deducted from on transfers.functionerc20Approve(address spender_,
uint256 value_
) publicvirtualreturns (bool) {
// Prevent granting 0x0 an ERC-20 allowance.if (spender_ ==address(0)) {
revert InvalidSpender();
}
allowance[msg.sender][spender_] = value_;
emit ERC20Events.Approval(msg.sender, spender_, value_);
returntrue;
}
/// @notice Function for ERC-721 approvalsfunctionsetApprovalForAll(address operator_,
bool approved_
) publicvirtual{
// Prevent approvals to 0x0.if (operator_ ==address(0)) {
revert InvalidOperator();
}
isApprovedForAll[msg.sender][operator_] = approved_;
emit ERC721Events.ApprovalForAll(msg.sender, operator_, approved_);
}
/// @notice Function for mixed transfers from an operator that may be different than 'from'./// @dev This function assumes the operator is attempting to transfer an ERC-721/// if valueOrId is a possible valid token id.functiontransferFrom(address from_,
address to_,
uint256 valueOrId_
) publicvirtualreturns (bool) {
if (_isValidTokenId(valueOrId_)) {
erc721TransferFrom(from_, to_, valueOrId_);
} else {
// Intention is to transfer as ERC-20 token (value).return erc20TransferFrom(from_, to_, valueOrId_);
}
returntrue;
}
/// @notice Function for ERC-721 transfers from./// @dev This function is recommended for ERC721 transfers.functionerc721TransferFrom(address from_,
address to_,
uint256 id_
) publicvirtual{
// Prevent minting tokens from 0x0.if (from_ ==address(0)) {
revert InvalidSender();
}
// Prevent burning tokens to 0x0.if (to_ ==address(0)) {
revert InvalidRecipient();
}
if (from_ != _getOwnerOf(id_)) {
revert Unauthorized();
}
// Check that the operator is either the sender or approved for the transfer.if (
msg.sender!= from_ &&!isApprovedForAll[from_][msg.sender] &&msg.sender!= getApproved[id_]
) {
revert Unauthorized();
}
// We only need to check ERC-721 transfer exempt status for the recipient// since the sender being ERC-721 transfer exempt means they have already// had their ERC-721s stripped away during the rebalancing process.if (erc721TransferExempt(to_)) {
revert RecipientIsERC721TransferExempt();
}
// Transfer 1 * units ERC-20 and 1 ERC-721 token.// ERC-721 transfer exemptions handled above. Can't make it to this point if either is transfer exempt.
_transferERC20(from_, to_, units);
_transferERC721(from_, to_, id_);
}
/// @notice Function for ERC-20 transfers from./// @dev This function is recommended for ERC20 transfersfunctionerc20TransferFrom(address from_,
address to_,
uint256 value_
) publicvirtualreturns (bool) {
// Prevent minting tokens from 0x0.if (from_ ==address(0)) {
revert InvalidSender();
}
// Prevent burning tokens to 0x0.if (to_ ==address(0)) {
revert InvalidRecipient();
}
uint256 allowed = allowance[from_][msg.sender];
// Check that the operator has sufficient allowance.if (allowed !=type(uint256).max) {
allowance[from_][msg.sender] = allowed - value_;
}
// Transferring ERC-20s directly requires the _transferERC20WithERC721 function.// Handles ERC-721 exemptions internally.return _transferERC20WithERC721(from_, to_, value_);
}
/// @notice Function for ERC-20 transfers./// @dev This function assumes the operator is attempting to transfer as ERC-20/// given this function is only supported on the ERC-20 interface./// Treats even large amounts that are valid ERC-721 ids as ERC-20s.functiontransfer(address to_,
uint256 value_
) publicvirtualreturns (bool) {
// Prevent burning tokens to 0x0.if (to_ ==address(0)) {
revert InvalidRecipient();
}
// Transferring ERC-20s directly requires the _transferERC20WithERC721 function.// Handles ERC-721 exemptions internally.return _transferERC20WithERC721(msg.sender, to_, value_);
}
/// @notice Function for ERC-721 transfers with contract support./// This function only supports moving valid ERC-721 ids, as it does not exist on the ERC-20/// spec and will revert otherwise.functionsafeTransferFrom(address from_,
address to_,
uint256 id_
) publicvirtual{
safeTransferFrom(from_, to_, id_, "");
}
/// @notice Function for ERC-721 transfers with contract support and callback data./// This function only supports moving valid ERC-721 ids, as it does not exist on the/// ERC-20 spec and will revert otherwise.functionsafeTransferFrom(address from_,
address to_,
uint256 id_,
bytesmemory data_
) publicvirtual{
if (!_isValidTokenId(id_)) {
revert InvalidTokenId();
}
erc721TransferFrom(from_, to_, id_);
if (
to_.code.length!=0&&
IERC721Receiver(to_).onERC721Received(
msg.sender,
from_,
id_,
data_
) !=
IERC721Receiver.onERC721Received.selector
) {
revert UnsafeRecipient();
}
}
functionsupportsInterface(bytes4 interfaceId
) publicviewvirtualreturns (bool) {
return
interfaceId ==type(IERC404).interfaceId||
interfaceId ==type(IERC165).interfaceId;
}
/// @notice Function for self-exemptionfunctionsetSelfERC721TransferExempt(bool state_) publicvirtual{
_setERC721TransferExempt(msg.sender, state_);
}
/// @notice Function to check if address is transfer exemptfunctionerc721TransferExempt(address target_
) publicviewvirtualreturns (bool) {
return target_ ==address(0) || _erc721TransferExempt[target_];
}
function_isValidTokenId(uint256 id_) internalviewreturns (bool) {
return id_ <= minted && id_ >0;
}
/// @notice This is the lowest level ERC-20 transfer function, which/// should be used for both normal ERC-20 transfers as well as minting./// Note that this function allows transfers to and from 0x0.function_transferERC20(address from_,
address to_,
uint256 value_
) internalvirtual{
// Minting is a special case for which we should not check the balance of// the sender, and we should increase the total supply.if (from_ ==address(0)) {
totalSupply += value_;
} else {
// Deduct value from sender's balance.
balanceOf[from_] -= value_;
}
if (to_ ==address(0)) {
totalSupply -= value_;
} else {
// Update the recipient's balance.// Can be unchecked because on mint, adding to totalSupply is checked, and on transfer balance deduction is checked.unchecked {
balanceOf[to_] += value_;
}
}
emit ERC20Events.Transfer(from_, to_, value_);
}
/// @notice Consolidated record keeping function for transferring ERC-721s./// @dev Assign the token to the new owner, and remove from the old owner./// Note that this function allows transfers to and from 0x0./// Does not handle ERC-721 exemptions.function_transferERC721(address from_,
address to_,
uint256 id_
) internalvirtual{
// If this is not a mint, handle record keeping for transfer from previous owner.if (from_ !=address(0)) {
// On transfer of an NFT, any previous approval is reset.delete getApproved[id_];
uint256 updatedId = _owned[from_][_owned[from_].length-1];
if (updatedId != id_) {
uint256 updatedIndex = _getOwnedIndex(id_);
// update _owned for sender
_owned[from_][updatedIndex] = updatedId;
// update index for the moved id
_setOwnedIndex(updatedId, updatedIndex);
}
// pop
_owned[from_].pop();
}
// Check if this is a burn.if (to_ !=address(0)) {
// If not a burn, update the owner of the token to the new owner.// Update owner of the token to the new owner.
_setOwnerOf(id_, to_);
// Push token onto the new owner's stack.
_owned[to_].push(id_);
// Update index for new owner's stack.
_setOwnedIndex(id_, _owned[to_].length-1);
} else {
// If this is a burn, reset the owner of the token to 0x0 by deleting the token from _ownedData.delete _ownedData[id_];
}
emit ERC721Events.Transfer(from_, to_, id_);
}
/// @notice Internal function for ERC-20 transfers. Also handles any ERC-721 transfers that may be required.// Handles ERC-721 exemptions.function_transferERC20WithERC721(address from_,
address to_,
uint256 value_
) internalvirtualreturns (bool) {
uint256 erc20BalanceOfSenderBefore = erc20BalanceOf(from_);
uint256 erc20BalanceOfReceiverBefore = erc20BalanceOf(to_);
_transferERC20(from_, to_, value_);
// Preload for gas savings on branchesbool isFromERC721TransferExempt = erc721TransferExempt(from_);
bool isToERC721TransferExempt = erc721TransferExempt(to_);
// Skip _withdrawAndStoreERC721 and/or _retrieveOrMintERC721 for ERC-721 transfer exempt addresses// 1) to save gas// 2) because ERC-721 transfer exempt addresses won't always have/need ERC-721s corresponding to their ERC20s.if (isFromERC721TransferExempt && isToERC721TransferExempt) {
// Case 1) Both sender and recipient are ERC-721 transfer exempt. No ERC-721s need to be transferred.// NOOP.
} elseif (isFromERC721TransferExempt) {
// Case 2) The sender is ERC-721 transfer exempt, but the recipient is not. Contract should not attempt// to transfer ERC-721s from the sender, but the recipient should receive ERC-721s// from the bank/minted for any whole number increase in their balance.// Only cares about whole number increments.uint256 tokensToRetrieveOrMint = (erc20BalanceOf(to_) / units) -
(erc20BalanceOfReceiverBefore / units);
for (uint256 i =0; i < tokensToRetrieveOrMint; ) {
_retrieveOrMintERC721(to_);
unchecked {
++i;
}
}
} elseif (isToERC721TransferExempt) {
// Case 3) The sender is not ERC-721 transfer exempt, but the recipient is. Contract should attempt// to withdraw and store ERC-721s from the sender, but the recipient should not// receive ERC-721s from the bank/minted.// Only cares about whole number increments.uint256 tokensToWithdrawAndStore = (erc20BalanceOfSenderBefore /
units) - (erc20BalanceOf(from_) / units);
for (uint256 i =0; i < tokensToWithdrawAndStore; ) {
_withdrawAndStoreERC721(from_);
unchecked {
++i;
}
}
} else {
// Case 4) Neither the sender nor the recipient are ERC-721 transfer exempt.// Strategy:// 1. First deal with the whole tokens. These are easy and will just be transferred.// 2. Look at the fractional part of the value:// a) If it causes the sender to lose a whole token that was represented by an NFT due to a// fractional part being transferred, withdraw and store an additional NFT from the sender.// b) If it causes the receiver to gain a whole new token that should be represented by an NFT// due to receiving a fractional part that completes a whole token, retrieve or mint an NFT to the recevier.// Whole tokens worth of ERC-20s get transferred as ERC-721s without any burning/minting.uint256 nftsToTransfer = value_ / units;
for (uint256 i =0; i < nftsToTransfer; ) {
// Pop from sender's ERC-721 stack and transfer them (LIFO)uint256 indexOfLastToken = _owned[from_].length-1;
uint256 tokenId = _owned[from_][indexOfLastToken];
_transferERC721(from_, to_, tokenId);
unchecked {
++i;
}
}
// If the transfer changes either the sender or the recipient's holdings from a fractional to a non-fractional// amount (or vice versa), adjust ERC-721s.// First check if the send causes the sender to lose a whole token that was represented by an ERC-721// due to a fractional part being transferred.//// Process:// Take the difference between the whole number of tokens before and after the transfer for the sender.// If that difference is greater than the number of ERC-721s transferred (whole units), then there was// an additional ERC-721 lost due to the fractional portion of the transfer.// If this is a self-send and the before and after balances are equal (not always the case but often),// then no ERC-721s will be lost here.if (
erc20BalanceOfSenderBefore /
units -
erc20BalanceOf(from_) /
units >
nftsToTransfer
) {
_withdrawAndStoreERC721(from_);
}
// Then, check if the transfer causes the receiver to gain a whole new token which requires gaining// an additional ERC-721.//// Process:// Take the difference between the whole number of tokens before and after the transfer for the recipient.// If that difference is greater than the number of ERC-721s transferred (whole units), then there was// an additional ERC-721 gained due to the fractional portion of the transfer.// Again, for self-sends where the before and after balances are equal, no ERC-721s will be gained here.if (
erc20BalanceOf(to_) /
units -
erc20BalanceOfReceiverBefore /
units >
nftsToTransfer
) {
_retrieveOrMintERC721(to_);
}
}
returntrue;
}
/// @notice Internal function for ERC20 minting/// @dev This function will allow minting of new ERC20s./// If mintCorrespondingERC721s_ is true, and the recipient is not ERC-721 exempt, it will/// also mint the corresponding ERC721s./// Handles ERC-721 exemptions.function_mintERC20(address to_, uint256 value_) internalvirtual{
/// You cannot mint to the zero address (you can't mint and immediately burn in the same transfer).if (to_ ==address(0)) {
revert InvalidRecipient();
}
_transferERC20WithERC721(address(0), to_, value_);
}
/// @notice Internal function for ERC-721 minting and retrieval from the bank./// @dev This function will allow minting of new ERC-721s up to the total fractional supply. It will/// first try to pull from the bank, and if the bank is empty, it will mint a new token./// Does not handle ERC-721 exemptions.function_retrieveOrMintERC721(address to_) internalvirtual{
if (to_ ==address(0)) {
revert InvalidRecipient();
}
uint256 id;
if (!_storedERC721Ids.empty()) {
// If there are any tokens in the bank, use those first.// Pop off the end of the queue (FIFO).
id = _storedERC721Ids.popBack();
} else {
// Otherwise, mint a new token, should not be able to go over the total fractional supply.++minted;
// Reserve max uint256 for approvalsif (minted ==type(uint256).max) {
revert MintLimitReached();
}
id = minted;
}
address erc721Owner = _getOwnerOf(id);
// The token should not already belong to anyone besides 0x0 or this contract.// If it does, something is wrong, as this should never happen.if (erc721Owner !=address(0)) {
revert AlreadyExists();
}
// Transfer the token to the recipient, either transferring from the contract's bank or minting.// Does not handle ERC-721 exemptions.
_transferERC721(erc721Owner, to_, id);
}
/// @notice Internal function for ERC-721 deposits to bank (this contract)./// @dev This function will allow depositing of ERC-721s to the bank, which can be retrieved by future minters.// Does not handle ERC-721 exemptions.function_withdrawAndStoreERC721(address from_) internalvirtual{
if (from_ ==address(0)) {
revert InvalidSender();
}
// Retrieve the latest token added to the owner's stack (LIFO).uint256 id = _owned[from_][_owned[from_].length-1];
// Transfer to 0x0.// Does not handle ERC-721 exemptions.
_transferERC721(from_, address(0), id);
_storedERC721Ids.pushFront(id);
}
/// @notice Initialization function to set pairs / etc, saving gas by avoiding mint / burn on unnecessary targetsfunction_setERC721TransferExempt(address target_,
bool state_
) internalvirtual{
if (target_ ==address(0)) {
revert InvalidExemption();
}
// Adjust the ERC721 balances of the target to respect exemption rules.// Despite this logic, it is still recommended practice to exempt prior to the target// having an active balance.if (state_) {
_clearERC721Balance(target_);
} else {
_reinstateERC721Balance(target_);
}
_erc721TransferExempt[target_] = state_;
}
/// @notice Function to reinstate balance on exemption removalfunction_reinstateERC721Balance(address target_) private{
uint256 expectedERC721Balance = erc20BalanceOf(target_) / units;
uint256 actualERC721Balance = erc721BalanceOf(target_);
for (uint256 i =0; i < expectedERC721Balance - actualERC721Balance; ) {
// Transfer ERC721 balance in from pool
_retrieveOrMintERC721(target_);
unchecked {
++i;
}
}
}
/// @notice Function to clear balance on exemption inclusionfunction_clearERC721Balance(address target_) private{
uint256 erc721Balance = erc721BalanceOf(target_);
for (uint256 i =0; i < erc721Balance; ) {
// Transfer out ERC721 balance
_withdrawAndStoreERC721(target_);
unchecked {
++i;
}
}
}
function_getOwnerOf(uint256 id_
) internalviewvirtualreturns (address ownerOf_) {
uint256 data = _ownedData[id_];
assembly {
ownerOf_ :=and(data, _BITMASK_ADDRESS)
}
}
function_setOwnerOf(uint256 id_, address owner_) internalvirtual{
uint256 data = _ownedData[id_];
assembly {
data :=add(
and(data, _BITMASK_OWNED_INDEX),
and(owner_, _BITMASK_ADDRESS)
)
}
_ownedData[id_] = data;
}
function_getOwnedIndex(uint256 id_
) internalviewvirtualreturns (uint256 ownedIndex_) {
uint256 data = _ownedData[id_];
assembly {
ownedIndex_ :=shr(160, data)
}
}
function_setOwnedIndex(uint256 id_, uint256 index_) internalvirtual{
uint256 data = _ownedData[id_];
if (index_ > _BITMASK_OWNED_INDEX >>160) {
revert OwnedIndexOverflow();
}
assembly {
data :=add(
and(data, _BITMASK_ADDRESS),
and(shl(160, index_), _BITMASK_OWNED_INDEX)
)
}
_ownedData[id_] = data;
}
}
Contract Source Code
File 5 of 15: ERC721Events.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.20;libraryERC721Events{
eventApprovalForAll(addressindexed owner,
addressindexed operator,
bool approved
);
eventApproval(addressindexed owner,
addressindexed spender,
uint256indexed id
);
eventTransfer(addressindexedfrom,
addressindexed to,
uint256indexed id
);
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)pragmasolidity ^0.8.20;/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/interfaceIERC721Receiver{
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/functiononERC721Received(address operator,
addressfrom,
uint256 tokenId,
bytescalldata data
) externalreturns (bytes4);
}
Contract Source Code
File 9 of 15: INonfungiblePositionManager.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity >=0.7.5;/// @title Non-fungible token for positions/// @notice Wraps Uniswap V3 positions in a non-fungible token interface which allows for them to be transferred/// and authorized.interfaceINonfungiblePositionManager{
/// @return Returns the address of the Uniswap V3 factoryfunctionfactory() externalviewreturns (address);
/// @return Returns the address of WETH9functionWETH9() externalviewreturns (address);
/// @notice Creates a new pool if it does not exist, then initializes if not initialized/// @dev This method can be bundled with others via IMulticall for the first action (e.g. mint) performed against a pool/// @param token0 The contract address of token0 of the pool/// @param token1 The contract address of token1 of the pool/// @param fee The fee amount of the v3 pool for the specified token pair/// @param sqrtPriceX96 The initial square root price of the pool as a Q64.96 value/// @return pool Returns the pool address based on the pair of tokens and fee, will return the newly created pool address if necessaryfunctioncreateAndInitializePoolIfNecessary(address token0,
address token1,
uint24 fee,
uint160 sqrtPriceX96
) externalpayablereturns (address pool);
structMintParams {
address token0;
address token1;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
/// @notice Creates a new position wrapped in a NFT/// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized/// a method does not exist, i.e. the pool is assumed to be initialized./// @param params The params necessary to mint a position, encoded as `MintParams` in calldata/// @return tokenId The ID of the token that represents the minted position/// @return liquidity The amount of liquidity for this position/// @return amount0 The amount of token0/// @return amount1 The amount of token1functionmint(
MintParams calldata params
)
externalpayablereturns (uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
structDecreaseLiquidityParams {
uint256 tokenId;
uint128 liquidity;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
/// @notice Decreases the amount of liquidity in a position and accounts it to the position/// @param params tokenId The ID of the token for which liquidity is being decreased,/// amount The amount by which liquidity will be decreased,/// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,/// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,/// deadline The time by which the transaction must be included to effect the change/// @return amount0 The amount of token0 accounted to the position's tokens owed/// @return amount1 The amount of token1 accounted to the position's tokens owedfunctiondecreaseLiquidity(
DecreaseLiquidityParams calldata params
) externalpayablereturns (uint256 amount0, uint256 amount1);
structCollectParams {
uint256 tokenId;
address recipient;
uint128 amount0Max;
uint128 amount1Max;
}
/// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient/// @param params tokenId The ID of the NFT for which tokens are being collected,/// recipient The account that should receive the tokens,/// amount0Max The maximum amount of token0 to collect,/// amount1Max The maximum amount of token1 to collect/// @return amount0 The amount of fees collected in token0/// @return amount1 The amount of fees collected in token1functioncollect(
CollectParams calldata params
) externalpayablereturns (uint256 amount0, uint256 amount1);
/// @notice Refunds any ETH balance held by this contract to the `msg.sender`/// @dev Useful for bundling with mint or increase liquidity that uses ether, or exact output swaps/// that use ether for the input amountfunctionrefundETH() externalpayable;
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)pragmasolidity ^0.8.20;/**
* @dev Standard math utilities missing in the Solidity language.
*/libraryMath{
/**
* @dev Returns the smallest of two numbers.
*/functionmin(uint256 a, uint256 b) internalpurereturns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/functionsqrt(uint256 a) internalpurereturns (uint256) {
if (a ==0) {
return0;
}
// 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);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/functionlog2(uint256 value) internalpurereturns (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 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/functionlog10(uint256 value) internalpurereturns (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;
}
}