Contract Name:
KillaCubsV2
Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "./KillaCubs/KillaCubsStaking.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
contract KillaCubsV2 is KillaCubsERC721, IURIManager {
using Strings for uint256;
using Strings for uint16;
constructor(
address bitsAddress,
address gearAddress,
address bearsAddress,
address passesAddress,
address kiltonAddress,
address labsAddress,
address superOwner
)
KillaCubsERC721(
bitsAddress,
gearAddress,
bearsAddress,
passesAddress,
kiltonAddress,
labsAddress,
superOwner
)
{
uriManager = IURIManager(this);
}
function toggleClaims(bool enabled) external onlyOwner {
claimsStarted = enabled;
}
function tokenURI(uint256 id) external view returns (string memory) {
Token memory token = resolveToken(id);
return uriManager.getTokenURI(id, token);
}
function getTokenURI(
uint256 id,
Token memory token
) public view returns (string memory) {
bool staked = token.stakeTimestamp > 0;
uint256 phase = calculateIncubationPhase(
token.incubationPhase,
token.stakeTimestamp,
token.generation
);
uint256 gen = token.generation;
if (laterGenerations[id] != 0) gen = laterGenerations[id];
if (staked) {
return
string(
abi.encodePacked(
baseURI,
gen == 0 ? "initial-" : "remix-",
id.toString(),
"-",
phase.toString(),
"-",
token.bit.toString()
)
);
}
string storage base = gen > finalizedGeneration || gen == 0
? baseURI
: baseURIFinalized;
return
string(
abi.encodePacked(
base,
gen == 0 ? "cubryo-" : "cub-",
id.toString(),
"-",
phase.toString()
)
);
}
function configureRoyalties(
address royaltyReceiver,
uint96 royaltyAmount
) external onlyOwner {
_setDefaultRoyalty(royaltyReceiver, royaltyAmount);
}
function finalizeGeneration(
uint256 gen,
string calldata uri
) external onlyOwner {
finalizedGeneration = gen;
baseURIFinalized = uri;
}
function setURIManager(address addr) external onlyOwner {
uriManager = IURIManager(addr);
}
function setBaseUri(string calldata uri) external onlyOwner {
baseURI = uri;
}
function copyTokensClaimed(address, uint256, uint256) external onlyOwner {
(bool success, ) = _delegatecall(airdropper, msg.data);
require(success, "delegatecall failed");
}
function copyTokensBatched(address, uint256, uint256) external onlyOwner {
(bool success, ) = _delegatecall(airdropper, msg.data);
require(success, "delegatecall failed");
}
function stake(uint256[] calldata) external {
(bool success, ) = _delegatecall(staker, msg.data);
require(success, "delegatecall failed");
}
function unstake(uint256[] calldata, bool) external {
(bool success, ) = _delegatecall(staker, msg.data);
require(success, "delegatecall failed");
}
function addBits(uint256[] calldata, uint16[] calldata) external {
(bool success, ) = _delegatecall(staker, msg.data);
require(success, "delegatecall failed");
}
function removeBits(uint256[] calldata) external {
(bool success, ) = _delegatecall(staker, msg.data);
require(success, "delegatecall failed");
}
function extractGear(uint256[] calldata) external {
(bool success, ) = _delegatecall(staker, msg.data);
require(success, "delegatecall failed");
}
function fastForward(
address,
uint256[] calldata,
uint256
) external onlyAuthority {
(bool success, ) = _delegatecall(staker, msg.data);
require(success, "delegatecall failed");
}
function configureStakingWindows(uint256, uint256) external onlyOwner {
(bool success, ) = _delegatecall(staker, msg.data);
require(success, "delegatecall failed");
}
function setIncubator(address) external onlyOwner {
(bool success, ) = _delegatecall(staker, msg.data);
require(success, "delegatecall failed");
}
function startNexGeneration() external onlyOwner {
(bool success, ) = _delegatecall(staker, msg.data);
require(success, "delegatecall failed");
}
function claim(uint256[] calldata, bool) public {
(bool success, ) = _delegatecall(claimer, msg.data);
require(success, "delegatecall failed");
}
function redeem(uint16, bool) external {
(bool success, ) = _delegatecall(claimer, msg.data);
require(success, "delegatecall failed");
}
fallback() external payable {
address extension = extensions[msg.sig];
require(extension != address(0));
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(
gas(),
extension,
0,
calldatasize(),
0,
0
)
returndatacopy(0, 0, returndatasize())
switch result
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
receive() external payable {}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "./KillaCubsERC721.sol";
abstract contract KillaCubsStaking is KillaCubsERC721 {
}
// 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
pragma solidity ^0.8.19;
import "operator-filter-registry/src/DefaultOperatorFilterer.sol";
import "./KillaCubsStorage.sol";
abstract contract KillaCubsERC721 is KillaCubsStorage {
constructor(
address bitsAddress,
address gearAddress,
address bearsAddress,
address passesAddress,
address kiltonAddress,
address labsAddress,
address superOwner
)
KillaCubsStorage(
bitsAddress,
gearAddress,
bearsAddress,
passesAddress,
kiltonAddress,
labsAddress,
superOwner
)
{
name = "KillaCubs";
symbol = "KillaCubs";
_setDefaultRoyalty(msg.sender, 500);
}
function _mint(address to, uint256 n, bool staked) internal {
uint256 tokenId = 3334 + counters.batched;
uint256 end = tokenId + n - 1;
if (end > 8888) revert NotAllowed();
Token storage token = tokens[tokenId];
token.owner = to;
counters.batched += uint16(n);
wallets[to].batchedMints += uint16(n);
if (staked) {
incubator.add(to, tokenId, n);
token.stakeTimestamp = uint32(block.timestamp);
counters.stakes += uint16(n);
wallets[to].stakes += uint16(n);
while (tokenId <= end) {
emit Transfer(address(0), to, tokenId);
emit Transfer(to, address(this), tokenId);
tokenId++;
}
} else {
wallets[to].balance += uint16(n);
while (tokenId <= end) {
emit Transfer(address(0), to, tokenId);
tokenId++;
}
}
}
function _mint(
address to,
uint256[] calldata tokenIds,
bool staked
) internal {
for (uint256 i = 0; i < tokenIds.length; i++) {
uint256 id = tokenIds[i];
Token storage token = tokens[id];
if (id == 0) revert NotAllowed();
if (token.owner != address(0)) revert NotAllowed();
if (token.linkedPrev != 0) revert NotAllowed();
token.owner = to;
emit Transfer(address(0), to, id);
if (staked) {
emit Transfer(to, address(this), id);
token.stakeTimestamp = uint32(block.timestamp);
}
if (i == 0) {
token.owner = to;
} else {
token.linkedPrev = uint16(tokenIds[i - 1]);
tokens[tokenIds[i - 1]].linkedNext = uint16(id);
}
}
counters.linked += uint16(tokenIds.length);
if (staked) {
counters.stakes += uint16(tokenIds.length);
wallets[to].stakes += uint16(tokenIds.length);
incubator.add(to, tokenIds);
} else {
wallets[to].balance += uint16(tokenIds.length);
}
wallets[to].linkedMints += uint16(tokenIds.length);
}
function totalSupply() public view virtual returns (uint256) {
return counters.linked + counters.batched;
}
function balanceOf(
address owner
) external view virtual returns (uint256 balance) {
if (owner == address(this)) return counters.stakes;
return wallets[owner].balance;
}
function ownerOf(uint256 id) public view virtual returns (address) {
Token memory token = resolveToken(id);
if (token.stakeTimestamp != 0) return address(this);
return token.owner;
}
function rightfulOwnerOf(
uint256 tokenId
) public view virtual returns (address) {
return resolveToken(tokenId).owner;
}
function resolveToken(uint256 id) public view returns (Token memory) {
Token memory token = tokens[id];
if (token.owner == address(0)) {
Token memory temp = token;
if (token.linkedPrev != 0) {
do token = tokens[token.linkedPrev]; while (
token.owner == address(0)
);
} else if (id > 3333 && id <= 3333 + counters.batched) {
do token = tokens[--id]; while (token.owner == address(0));
} else {
revert NonExistentToken();
}
token.bit = temp.bit;
token.linkedNext = temp.linkedNext;
token.linkedPrev = temp.linkedPrev;
}
return token;
}
function resolveTokens(
uint256[] calldata ids
) public view returns (Token[] memory) {
Token[] memory ret = new Token[](ids.length);
bool skip = false;
Token memory token;
for (uint256 i = 0; i < ids.length; i++) {
uint256 id = ids[i];
if (skip) skip = false;
else token = resolveToken(id);
ret[i] = token;
uint256 nextId;
if (token.linkedNext != 0) {
nextId = token.linkedNext;
} else if (id > 3333 && id < 3333 + counters.batched) {
nextId = id + 1;
} else {
continue;
}
if (tokens[nextId].owner != address(0)) continue;
if (i + 1 < ids.length && ids[i + 1] == nextId) {
skip = true;
token.bit = tokens[nextId].bit;
token.linkedNext = tokens[nextId].linkedNext;
token.linkedPrev = tokens[nextId].linkedPrev;
continue;
}
}
return ret;
}
function safeTransferFrom(
address from,
address to,
uint256 id,
bytes memory data
) public virtual {
transferFrom(from, to, id);
if (to.code.length != 0)
if (!_checkOnERC721Received(from, to, id, data))
revert TransferToNonERC721ReceiverImplementer();
}
function safeTransferFrom(
address from,
address to,
uint256 id
) public virtual {
transferFrom(from, to, id);
if (to.code.length != 0)
if (!_checkOnERC721Received(from, to, id, ""))
revert TransferToNonERC721ReceiverImplementer();
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual onlyAllowedOperator(from) {
if (to == from) revert NotAllowed();
if (to == address(0)) revert NotAllowed();
Token memory token = resolveToken(id);
if (token.stakeTimestamp > 0 || token.owner != from)
revert NotAllowed();
if (msg.sender != token.owner) {
if (
!operatorApprovals[token.owner][msg.sender] &&
tokenApprovals[id] != msg.sender
) revert NotAllowed();
}
if (tokenApprovals[id] != address(0)) {
delete tokenApprovals[id];
emit Approval(from, address(0), id);
}
emit Transfer(token.owner, to, id);
_bakeNextToken(token, id);
token.owner = to;
wallets[from].balance--;
wallets[to].balance++;
tokens[id] = token;
}
function _bakeNextToken(Token memory current, uint256 id) internal {
uint256 nextId;
if (current.linkedNext != 0) {
nextId = current.linkedNext;
} else if (id > 3333) {
nextId = id + 1;
if (nextId > 3333 + counters.batched) return;
} else {
return;
}
Token memory temp = tokens[nextId];
if (temp.owner != address(0)) return;
tokens[nextId] = current;
tokens[nextId].linkedNext = temp.linkedNext;
tokens[nextId].linkedPrev = temp.linkedPrev;
tokens[nextId].bit = temp.bit;
}
function approve(
address to,
uint256 id
) public virtual onlyAllowedOperatorApproval(to) {
address owner = ownerOf(id);
if (msg.sender != owner) {
if (!isApprovedForAll(owner, msg.sender)) {
revert NotAllowed();
}
}
tokenApprovals[id] = to;
emit Approval(msg.sender, to, id);
}
function setApprovalForAll(
address operator,
bool approved
) public virtual onlyAllowedOperatorApproval(operator) {
operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function getApproved(
uint256 id
) external view virtual returns (address operator) {
return tokenApprovals[id];
}
function isApprovedForAll(
address owner,
address operator
) public view virtual returns (bool) {
return operatorApprovals[owner][operator];
}
function supportsInterface(
bytes4 interfaceId
) public view virtual override returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165
interfaceId == 0x80ac58cd || // ERC721
interfaceId == 0x5b5e139f || // ERC721Metadata;
interfaceId == 0x4e2312e0 || // ERC1155Receiver
interfaceId == 0x2a55205a; // ERC2981
}
function _checkOnERC721Received(
address from,
address to,
uint256 id,
bytes memory data
) private returns (bool) {
try
IERC721Receiver(to).onERC721Received(msg.sender, from, id, data)
returns (bytes4 retval) {
return retval == IERC721Receiver.onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert("ERC721: transfer to non ERC721Receiver implementer");
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes calldata
) external pure returns (bytes4) {
return
bytes4(
keccak256(
"onERC1155Received(address,address,uint256,uint256,bytes)"
)
);
}
function calculateIncubationPhase(
uint256 phase,
uint256 ts,
uint256 gen
) public view returns (uint256) {
if (ts != 0) {
phase += (block.timestamp - ts) / 1 weeks;
}
uint256 max = gen == 0
? initialIncubationLength
: remixIncubationLength;
if (phase > max) return max;
return phase;
}
function getIncubationPhase(uint256 id) public view returns (uint256) {
Token memory token = resolveToken(id);
return
calculateIncubationPhase(
token.incubationPhase,
token.stakeTimestamp,
token.generation
);
}
function getGeneration(uint256 id) public view returns (uint256) {
if (laterGenerations[id] != 0) return laterGenerations[id];
Token memory token = resolveToken(id);
return token.generation;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/common/ERC2981.sol";
import "operator-filter-registry/src/DefaultOperatorFilterer.sol";
import "../SuperOwnable.sol";
interface IKillaPasses {
function burn(uint256 typeId, address owner, uint256 n) external;
}
interface IURIManager {
function getTokenURI(
uint256 id,
Token memory token
) external view returns (string memory);
}
interface IKILLABITS {
function detachUpgrade(uint256 token) external;
function tokenUpgrade(uint256 token) external view returns (uint64);
function transferFrom(address from, address to, uint256 tokenId) external;
}
interface IKILLAGEAR {
function detokenize(
address addr,
uint256[] calldata types,
uint256[] calldata amounts
) external;
}
struct Token {
address owner;
uint16 linkedNext;
uint16 linkedPrev;
uint32 stakeTimestamp;
uint8 generation;
uint8 incubationPhase;
uint16 bit;
}
struct Wallet {
uint16 balance;
uint16 stakes;
uint16 linkedMints;
uint16 batchedMints;
uint16 allowlistMints;
uint16 privateMints;
uint16 holderMints;
uint16 redeems;
}
struct MintCounters {
uint16 linked;
uint16 batched;
uint16 redeems;
uint16 stakes;
}
interface IIncubator {
function add(address owner, uint256[] calldata tokenIds) external;
function add(address owner, uint256 start, uint256 count) external;
function remove(address owner, uint256[] calldata tokenIds) external;
function remove(address owner, uint256 start, uint256 count) external;
}
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
interface IERC721 {
function ownerOf(uint256 tokenId) external view returns (address owner);
}
abstract contract KillaCubsStorage is
DefaultOperatorFilterer,
SuperOwnable,
ERC2981
{
string public name;
string public symbol;
uint256 public activeGeneration = 1;
uint256 public initialIncubationLength = 8;
uint256 public remixIncubationLength = 4;
IIncubator public incubator;
MintCounters public counters;
mapping(address => Wallet) public wallets;
mapping(uint256 => Token) public tokens;
mapping(uint256 => address) internal tokenApprovals;
mapping(address => mapping(address => bool)) internal operatorApprovals;
IKILLABITS public bitsContract;
IKILLAGEAR public gearContract;
IERC721 public bears;
IKillaPasses public passes;
IERC721 public kilton;
IERC721 public labs;
bool public claimsStarted;
mapping(uint256 => bool) public bitsUsed;
mapping(uint256 => uint256) public laterGenerations;
address public airdropper;
address public staker;
address public claimer;
IURIManager public uriManager;
string public baseURI;
string public baseURIFinalized;
uint256 public finalizedGeneration;
mapping(bytes4 => address) extensions;
mapping(uint256 => address) externalStorage;
error TransferToNonERC721ReceiverImplementer();
error NonExistentToken();
error NotAllowed();
error Overflow();
error ClaimNotStarted();
event BitsAdded(uint256[] indexed tokens, uint16[] indexed bits);
event BitRemoved(uint256 indexed token, uint16 indexed bit);
event FastForwarded(uint256[] indexed tokens, uint256 indexed numberOfDays);
event Transfer(
address indexed from,
address indexed to,
uint256 indexed tokenId
);
event Approval(
address indexed owner,
address indexed approved,
uint256 indexed tokenId
);
event ApprovalForAll(
address indexed owner,
address indexed operator,
bool approved
);
constructor(
address bitsAddress,
address gearAddress,
address bearsAddress,
address passesAddress,
address kiltonAddress,
address labsAddress,
address superOwner
) SuperOwnable(superOwner) {
bitsContract = IKILLABITS(bitsAddress);
gearContract = IKILLAGEAR(gearAddress);
bears = IERC721(bearsAddress);
passes = IKillaPasses(passesAddress);
kilton = IERC721(kiltonAddress);
labs = IERC721(labsAddress);
}
function setAirdropper(address a) external onlyOwner {
airdropper = a;
}
function setStaker(address a) external onlyOwner {
staker = a;
}
function setClaimer(address a) external onlyOwner {
claimer = a;
}
function setExtension(bytes4 id, address a) external onlyOwner {
extensions[id] = a;
}
function setExternalStorage(uint256 id, address a) external onlyOwner {
externalStorage[id] = a;
}
function _delegatecall(
address target,
bytes memory data
) internal returns (bool, bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
if (!success) {
if (returndata.length == 0) revert();
assembly {
revert(add(32, returndata), mload(returndata))
}
}
return (success, returndata);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {OperatorFilterer} from "./OperatorFilterer.sol";
/**
* @title DefaultOperatorFilterer
* @notice Inherits from OperatorFilterer and automatically subscribes to the default OpenSea subscription.
*/
abstract contract DefaultOperatorFilterer is OperatorFilterer {
address constant DEFAULT_SUBSCRIPTION = address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);
constructor() OperatorFilterer(DEFAULT_SUBSCRIPTION, true) {}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
abstract contract SuperOwnable {
address public owner;
address public superOwner;
mapping(address => bool) authorities;
error Denied();
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
constructor(address superOwner_) {
_transferOwnership(msg.sender);
superOwner = superOwner_;
}
modifier onlyOwner() {
if (msg.sender != owner && msg.sender != superOwner) revert Denied();
_;
}
modifier onlySuperOwner() {
if (msg.sender != superOwner) revert Denied();
_;
}
modifier onlyAuthority() {
if (!authorities[msg.sender] && msg.sender != owner) revert Denied();
_;
}
function transferOwnership(address addr) public virtual onlyOwner {
_transferOwnership(addr);
}
function _transferOwnership(address addr) internal virtual {
address oldOwner = owner;
owner = addr;
emit OwnershipTransferred(oldOwner, addr);
}
function setSuperOwner(address addr) public onlySuperOwner {
if (addr == address(0)) revert Denied();
superOwner = addr;
}
function toggleAuthority(address addr, bool enabled) public onlyOwner {
authorities[addr] = enabled;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/common/ERC2981.sol)
pragma solidity ^0.8.0;
import "../../interfaces/IERC2981.sol";
import "../../utils/introspection/ERC165.sol";
/**
* @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
*
* Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
* specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
*
* Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
* fee is specified in basis points by default.
*
* IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
* https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
* voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
*
* _Available since v4.5._
*/
abstract contract ERC2981 is IERC2981, ERC165 {
struct RoyaltyInfo {
address receiver;
uint96 royaltyFraction;
}
RoyaltyInfo private _defaultRoyaltyInfo;
mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @inheritdoc IERC2981
*/
function royaltyInfo(uint256 _tokenId, uint256 _salePrice) public view virtual override returns (address, uint256) {
RoyaltyInfo memory royalty = _tokenRoyaltyInfo[_tokenId];
if (royalty.receiver == address(0)) {
royalty = _defaultRoyaltyInfo;
}
uint256 royaltyAmount = (_salePrice * royalty.royaltyFraction) / _feeDenominator();
return (royalty.receiver, royaltyAmount);
}
/**
* @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
* fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
* override.
*/
function _feeDenominator() internal pure virtual returns (uint96) {
return 10000;
}
/**
* @dev Sets the royalty information that all ids in this contract will default to.
*
* Requirements:
*
* - `receiver` cannot be the zero address.
* - `feeNumerator` cannot be greater than the fee denominator.
*/
function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
require(receiver != address(0), "ERC2981: invalid receiver");
_defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
}
/**
* @dev Removes default royalty information.
*/
function _deleteDefaultRoyalty() internal virtual {
delete _defaultRoyaltyInfo;
}
/**
* @dev Sets the royalty information for a specific token id, overriding the global default.
*
* Requirements:
*
* - `receiver` cannot be the zero address.
* - `feeNumerator` cannot be greater than the fee denominator.
*/
function _setTokenRoyalty(
uint256 tokenId,
address receiver,
uint96 feeNumerator
) internal virtual {
require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
require(receiver != address(0), "ERC2981: Invalid parameters");
_tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
}
/**
* @dev Resets royalty information for the token id back to the global default.
*/
function _resetTokenRoyalty(uint256 tokenId) internal virtual {
delete _tokenRoyaltyInfo[tokenId];
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {IOperatorFilterRegistry} from "./IOperatorFilterRegistry.sol";
/**
* @title OperatorFilterer
* @notice Abstract contract whose constructor automatically registers and optionally subscribes to or copies another
* registrant's entries in the OperatorFilterRegistry.
* @dev This smart contract is meant to be inherited by token contracts so they can use the following:
* - `onlyAllowedOperator` modifier for `transferFrom` and `safeTransferFrom` methods.
* - `onlyAllowedOperatorApproval` modifier for `approve` and `setApprovalForAll` methods.
*/
abstract contract OperatorFilterer {
error OperatorNotAllowed(address operator);
IOperatorFilterRegistry public constant OPERATOR_FILTER_REGISTRY =
IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);
constructor(address subscriptionOrRegistrantToCopy, bool subscribe) {
// If an inheriting token contract is deployed to a network without the registry deployed, the modifier
// will not revert, but the contract will need to be registered with the registry once it is deployed in
// order for the modifier to filter addresses.
if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
if (subscribe) {
OPERATOR_FILTER_REGISTRY.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
} else {
if (subscriptionOrRegistrantToCopy != address(0)) {
OPERATOR_FILTER_REGISTRY.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy);
} else {
OPERATOR_FILTER_REGISTRY.register(address(this));
}
}
}
}
modifier onlyAllowedOperator(address from) virtual {
// Allow spending tokens from addresses with balance
// Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
// from an EOA.
if (from != msg.sender) {
_checkFilterOperator(msg.sender);
}
_;
}
modifier onlyAllowedOperatorApproval(address operator) virtual {
_checkFilterOperator(operator);
_;
}
function _checkFilterOperator(address operator) internal view virtual {
// Check registry code length to facilitate testing in environments without a deployed registry.
if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
revert OperatorNotAllowed(operator);
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
interface IOperatorFilterRegistry {
function isOperatorAllowed(address registrant, address operator) external view returns (bool);
function register(address registrant) external;
function registerAndSubscribe(address registrant, address subscription) external;
function registerAndCopyEntries(address registrant, address registrantToCopy) external;
function unregister(address addr) external;
function updateOperator(address registrant, address operator, bool filtered) external;
function updateOperators(address registrant, address[] calldata operators, bool filtered) external;
function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external;
function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external;
function subscribe(address registrant, address registrantToSubscribe) external;
function unsubscribe(address registrant, bool copyExistingEntries) external;
function subscriptionOf(address addr) external returns (address registrant);
function subscribers(address registrant) external returns (address[] memory);
function subscriberAt(address registrant, uint256 index) external returns (address);
function copyEntriesOf(address registrant, address registrantToCopy) external;
function isOperatorFiltered(address registrant, address operator) external returns (bool);
function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
function filteredOperators(address addr) external returns (address[] memory);
function filteredCodeHashes(address addr) external returns (bytes32[] memory);
function filteredOperatorAt(address registrant, uint256 index) external returns (address);
function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
function isRegistered(address addr) external returns (bool);
function codeHashOf(address addr) external returns (bytes32);
}
// 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
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
// 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
// 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);
}
}
}