Contract Name:
KeepersDiamond
Contract Source Code:
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
pragma solidity ^0.8.0;
/**
* @title Counters
* @author Matt Condon (@shrugs)
* @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
* of elements in a mapping, issuing ERC721 ids, or counting request ids.
*
* Include with `using Counters for Counters.Counter;`
*/
library Counters {
struct Counter {
// This variable should never be directly accessed by users of the library: interactions must be restricted to
// the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
// this feature: see https://github.com/ethereum/solidity/issues/4637
uint256 _value; // default: 0
}
function current(Counter storage counter) internal view returns (uint256) {
return counter._value;
}
function increment(Counter storage counter) internal {
unchecked {
counter._value += 1;
}
}
function decrement(Counter storage counter) internal {
uint256 value = counter._value;
require(value > 0, "Counter: decrement overflow");
unchecked {
counter._value = value - 1;
}
}
function reset(Counter storage counter) internal {
counter._value = 0;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { EnumerableSet } from '../../data/EnumerableSet.sol';
import { AddressUtils } from '../../utils/AddressUtils.sol';
import { UintUtils } from '../../utils/UintUtils.sol';
import { IAccessControlInternal } from './IAccessControlInternal.sol';
import { AccessControlStorage } from './AccessControlStorage.sol';
/**
* @title Role-based access control system
* @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)
*/
abstract contract AccessControlInternal is IAccessControlInternal {
using AddressUtils for address;
using EnumerableSet for EnumerableSet.AddressSet;
using UintUtils for uint256;
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/*
* @notice query whether role is assigned to account
* @param role role to query
* @param account account to query
* @return whether role is assigned to account
*/
function _hasRole(
bytes32 role,
address account
) internal view virtual returns (bool) {
return
AccessControlStorage.layout().roles[role].members.contains(account);
}
/**
* @notice revert if sender does not have given role
* @param role role to query
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, msg.sender);
}
/**
* @notice revert if given account does not have given role
* @param role role to query
* @param account to query
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!_hasRole(role, account)) {
revert(
string(
abi.encodePacked(
'AccessControl: account ',
account.toString(),
' is missing role ',
uint256(role).toHexString(32)
)
)
);
}
}
/*
* @notice query admin role for given role
* @param role role to query
* @return admin role
*/
function _getRoleAdmin(
bytes32 role
) internal view virtual returns (bytes32) {
return AccessControlStorage.layout().roles[role].adminRole;
}
/**
* @notice set role as admin role
* @param role role to set
* @param adminRole admin role to set
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = _getRoleAdmin(role);
AccessControlStorage.layout().roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/*
* @notice assign role to given account
* @param role role to assign
* @param account recipient of role assignment
*/
function _grantRole(bytes32 role, address account) internal virtual {
AccessControlStorage.layout().roles[role].members.add(account);
emit RoleGranted(role, account, msg.sender);
}
/*
* @notice unassign role from given account
* @param role role to unassign
* @parm account
*/
function _revokeRole(bytes32 role, address account) internal virtual {
AccessControlStorage.layout().roles[role].members.remove(account);
emit RoleRevoked(role, account, msg.sender);
}
/**
* @notice relinquish role
* @param role role to relinquish
*/
function _renounceRole(bytes32 role) internal virtual {
_revokeRole(role, msg.sender);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { EnumerableSet } from '../../data/EnumerableSet.sol';
library AccessControlStorage {
struct RoleData {
EnumerableSet.AddressSet members;
bytes32 adminRole;
}
struct Layout {
mapping(bytes32 => RoleData) roles;
}
bytes32 internal constant DEFAULT_ADMIN_ROLE = 0x00;
bytes32 internal constant STORAGE_SLOT =
keccak256('solidstate.contracts.storage.AccessControl');
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly {
l.slot := slot
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title Partial AccessControl interface needed by internal functions
*/
interface IAccessControlInternal {
event RoleAdminChanged(
bytes32 indexed role,
bytes32 indexed previousAdminRole,
bytes32 indexed newAdminRole
);
event RoleGranted(
bytes32 indexed role,
address indexed account,
address indexed sender
);
event RoleRevoked(
bytes32 indexed role,
address indexed account,
address indexed sender
);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { IERC173 } from '../../interfaces/IERC173.sol';
import { IOwnableInternal } from './IOwnableInternal.sol';
interface IOwnable is IOwnableInternal, IERC173 {}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { IERC173Internal } from '../../interfaces/IERC173Internal.sol';
interface IOwnableInternal is IERC173Internal {
error Ownable__NotOwner();
error Ownable__NotTransitiveOwner();
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { IOwnable } from './IOwnable.sol';
import { ISafeOwnableInternal } from './ISafeOwnableInternal.sol';
interface ISafeOwnable is ISafeOwnableInternal, IOwnable {
/**
* @notice get the nominated owner who has permission to call acceptOwnership
*/
function nomineeOwner() external view returns (address);
/**
* @notice accept transfer of contract ownership
*/
function acceptOwnership() external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { IOwnableInternal } from './IOwnableInternal.sol';
interface ISafeOwnableInternal is IOwnableInternal {
error SafeOwnable__NotNomineeOwner();
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { IERC173 } from '../../interfaces/IERC173.sol';
import { IOwnable } from './IOwnable.sol';
import { OwnableInternal } from './OwnableInternal.sol';
/**
* @title Ownership access control based on ERC173
*/
abstract contract Ownable is IOwnable, OwnableInternal {
/**
* @inheritdoc IERC173
*/
function owner() public view virtual returns (address) {
return _owner();
}
/**
* @inheritdoc IERC173
*/
function transferOwnership(address account) public virtual onlyOwner {
_transferOwnership(account);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { IERC173 } from '../../interfaces/IERC173.sol';
import { AddressUtils } from '../../utils/AddressUtils.sol';
import { IOwnableInternal } from './IOwnableInternal.sol';
import { OwnableStorage } from './OwnableStorage.sol';
abstract contract OwnableInternal is IOwnableInternal {
using AddressUtils for address;
modifier onlyOwner() {
if (msg.sender != _owner()) revert Ownable__NotOwner();
_;
}
modifier onlyTransitiveOwner() {
if (msg.sender != _transitiveOwner())
revert Ownable__NotTransitiveOwner();
_;
}
function _owner() internal view virtual returns (address) {
return OwnableStorage.layout().owner;
}
function _transitiveOwner() internal view virtual returns (address owner) {
owner = _owner();
while (owner.isContract()) {
try IERC173(owner).owner() returns (address transitiveOwner) {
owner = transitiveOwner;
} catch {
break;
}
}
}
function _transferOwnership(address account) internal virtual {
_setOwner(account);
}
function _setOwner(address account) internal virtual {
OwnableStorage.Layout storage l = OwnableStorage.layout();
emit OwnershipTransferred(l.owner, account);
l.owner = account;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
library OwnableStorage {
struct Layout {
address owner;
}
bytes32 internal constant STORAGE_SLOT =
keccak256('solidstate.contracts.storage.Ownable');
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly {
l.slot := slot
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { Ownable } from './Ownable.sol';
import { ISafeOwnable } from './ISafeOwnable.sol';
import { OwnableInternal } from './OwnableInternal.sol';
import { SafeOwnableInternal } from './SafeOwnableInternal.sol';
/**
* @title Ownership access control based on ERC173 with ownership transfer safety check
*/
abstract contract SafeOwnable is ISafeOwnable, Ownable, SafeOwnableInternal {
/**
* @inheritdoc ISafeOwnable
*/
function nomineeOwner() public view virtual returns (address) {
return _nomineeOwner();
}
/**
* @inheritdoc ISafeOwnable
*/
function acceptOwnership() public virtual onlyNomineeOwner {
_acceptOwnership();
}
function _transferOwnership(
address account
) internal virtual override(OwnableInternal, SafeOwnableInternal) {
super._transferOwnership(account);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { ISafeOwnableInternal } from './ISafeOwnableInternal.sol';
import { OwnableInternal } from './OwnableInternal.sol';
import { SafeOwnableStorage } from './SafeOwnableStorage.sol';
abstract contract SafeOwnableInternal is ISafeOwnableInternal, OwnableInternal {
modifier onlyNomineeOwner() {
if (msg.sender != _nomineeOwner())
revert SafeOwnable__NotNomineeOwner();
_;
}
/**
* @notice get the nominated owner who has permission to call acceptOwnership
*/
function _nomineeOwner() internal view virtual returns (address) {
return SafeOwnableStorage.layout().nomineeOwner;
}
/**
* @notice accept transfer of contract ownership
*/
function _acceptOwnership() internal virtual {
_setOwner(msg.sender);
delete SafeOwnableStorage.layout().nomineeOwner;
}
/**
* @notice grant permission to given address to claim contract ownership
*/
function _transferOwnership(address account) internal virtual override {
_setNomineeOwner(account);
}
/**
* @notice set nominee owner
*/
function _setNomineeOwner(address account) internal virtual {
SafeOwnableStorage.layout().nomineeOwner = account;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
library SafeOwnableStorage {
struct Layout {
address nomineeOwner;
}
bytes32 internal constant STORAGE_SLOT =
keccak256('solidstate.contracts.storage.SafeOwnable');
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly {
l.slot := slot
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
/**
* @title Set implementation with enumeration functions
* @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)
*/
library EnumerableSet {
error EnumerableSet__IndexOutOfBounds();
struct Set {
bytes32[] _values;
// 1-indexed to allow 0 to signify nonexistence
mapping(bytes32 => uint256) _indexes;
}
struct Bytes32Set {
Set _inner;
}
struct AddressSet {
Set _inner;
}
struct UintSet {
Set _inner;
}
function at(
Bytes32Set storage set,
uint256 index
) internal view returns (bytes32) {
return _at(set._inner, index);
}
function at(
AddressSet storage set,
uint256 index
) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
function at(
UintSet storage set,
uint256 index
) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
function contains(
Bytes32Set storage set,
bytes32 value
) internal view returns (bool) {
return _contains(set._inner, value);
}
function contains(
AddressSet storage set,
address value
) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
function contains(
UintSet storage set,
uint256 value
) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
function indexOf(
Bytes32Set storage set,
bytes32 value
) internal view returns (uint256) {
return _indexOf(set._inner, value);
}
function indexOf(
AddressSet storage set,
address value
) internal view returns (uint256) {
return _indexOf(set._inner, bytes32(uint256(uint160(value))));
}
function indexOf(
UintSet storage set,
uint256 value
) internal view returns (uint256) {
return _indexOf(set._inner, bytes32(value));
}
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function add(
Bytes32Set storage set,
bytes32 value
) internal returns (bool) {
return _add(set._inner, value);
}
function add(
AddressSet storage set,
address value
) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
function remove(
Bytes32Set storage set,
bytes32 value
) internal returns (bool) {
return _remove(set._inner, value);
}
function remove(
AddressSet storage set,
address value
) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
function remove(
UintSet storage set,
uint256 value
) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
function toArray(
Bytes32Set storage set
) internal view returns (bytes32[] memory) {
return set._inner._values;
}
function toArray(
AddressSet storage set
) internal view returns (address[] memory) {
bytes32[] storage values = set._inner._values;
address[] storage array;
assembly {
array.slot := values.slot
}
return array;
}
function toArray(
UintSet storage set
) internal view returns (uint256[] memory) {
bytes32[] storage values = set._inner._values;
uint256[] storage array;
assembly {
array.slot := values.slot
}
return array;
}
function _at(
Set storage set,
uint256 index
) private view returns (bytes32) {
if (index >= set._values.length)
revert EnumerableSet__IndexOutOfBounds();
return set._values[index];
}
function _contains(
Set storage set,
bytes32 value
) private view returns (bool) {
return set._indexes[value] != 0;
}
function _indexOf(
Set storage set,
bytes32 value
) private view returns (uint256) {
unchecked {
return set._indexes[value] - 1;
}
}
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
function _add(
Set storage set,
bytes32 value
) private returns (bool status) {
if (!_contains(set, value)) {
set._values.push(value);
set._indexes[value] = set._values.length;
status = true;
}
}
function _remove(
Set storage set,
bytes32 value
) private returns (bool status) {
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
unchecked {
bytes32 last = set._values[set._values.length - 1];
// move last value to now-vacant index
set._values[valueIndex - 1] = last;
set._indexes[last] = valueIndex;
}
// clear last index
set._values.pop();
delete set._indexes[value];
status = true;
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { IERC165Internal } from './IERC165Internal.sol';
/**
* @title ERC165 interface registration interface
* @dev see https://eips.ethereum.org/EIPS/eip-165
*/
interface IERC165 is IERC165Internal {
/**
* @notice query whether contract has registered support for given interface
* @param interfaceId interface id
* @return bool whether interface is supported
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
/**
* @title ERC165 interface registration interface
*/
interface IERC165Internal {
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { IERC173Internal } from './IERC173Internal.sol';
/**
* @title Contract ownership standard interface
* @dev see https://eips.ethereum.org/EIPS/eip-173
*/
interface IERC173 is IERC173Internal {
/**
* @notice get the ERC173 contract owner
* @return contract owner
*/
function owner() external view returns (address);
/**
* @notice transfer contract ownership to new account
* @param account address of new owner
*/
function transferOwnership(address account) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
/**
* @title Partial ERC173 interface needed by internal functions
*/
interface IERC173Internal {
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { IERC165 } from './IERC165.sol';
import { IERC2981Internal } from './IERC2981Internal.sol';
/**
* @title ERC2981 interface
* @dev see https://eips.ethereum.org/EIPS/eip-2981
*/
interface IERC2981 is IERC2981Internal, IERC165 {
/**
* @notice called with the sale price to determine how much royalty is owed and to whom
* @param tokenId the ERC721 or ERC1155 token id to query for royalty information
* @param salePrice the sale price of the given asset
* @return receiever rightful recipient of royalty
* @return royaltyAmount amount of royalty owed
*/
function royaltyInfo(
uint256 tokenId,
uint256 salePrice
) external view returns (address receiever, uint256 royaltyAmount);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
/**
* @title ERC2981 interface
*/
interface IERC2981Internal {
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { IERC165 } from './IERC165.sol';
import { IERC721Internal } from './IERC721Internal.sol';
/**
* @title ERC721 interface
* @dev see https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721 is IERC721Internal, IERC165 {
/**
* @notice query the balance of given address
* @return balance quantity of tokens held
*/
function balanceOf(address account) external view returns (uint256 balance);
/**
* @notice query the owner of given token
* @param tokenId token to query
* @return owner token owner
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @notice transfer token between given addresses, checking for ERC721Receiver implementation if applicable
* @param from sender of token
* @param to receiver of token
* @param tokenId token id
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external payable;
/**
* @notice transfer token between given addresses, checking for ERC721Receiver implementation if applicable
* @param from sender of token
* @param to receiver of token
* @param tokenId token id
* @param data data payload
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external payable;
/**
* @notice transfer token between given addresses, without checking for ERC721Receiver implementation if applicable
* @param from sender of token
* @param to receiver of token
* @param tokenId token id
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) external payable;
/**
* @notice grant approval to given account to spend token
* @param operator address to be approved
* @param tokenId token to approve
*/
function approve(address operator, uint256 tokenId) external payable;
/**
* @notice get approval status for given token
* @param tokenId token to query
* @return operator address approved to spend token
*/
function getApproved(
uint256 tokenId
) external view returns (address operator);
/**
* @notice grant approval to or revoke approval from given account to spend all tokens held by sender
* @param operator address to be approved
* @param status approval status
*/
function setApprovalForAll(address operator, bool status) external;
/**
* @notice query approval status of given operator with respect to given address
* @param account address to query for approval granted
* @param operator address to query for approval received
* @return status whether operator is approved to spend tokens held by account
*/
function isApprovedForAll(
address account,
address operator
) external view returns (bool status);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
/**
* @title Partial ERC721 interface needed by internal functions
*/
interface IERC721Internal {
event Transfer(
address indexed from,
address indexed to,
uint256 indexed tokenId
);
event Approval(
address indexed owner,
address indexed operator,
uint256 indexed tokenId
);
event ApprovalForAll(
address indexed owner,
address indexed operator,
bool approved
);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { IERC165 } from '../../../interfaces/IERC165.sol';
import { IERC165Base } from './IERC165Base.sol';
import { ERC165BaseInternal } from './ERC165BaseInternal.sol';
import { ERC165BaseStorage } from './ERC165BaseStorage.sol';
/**
* @title ERC165 implementation
*/
abstract contract ERC165Base is IERC165Base, ERC165BaseInternal {
/**
* @inheritdoc IERC165
*/
function supportsInterface(bytes4 interfaceId) public view returns (bool) {
return _supportsInterface(interfaceId);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { IERC165BaseInternal } from './IERC165BaseInternal.sol';
import { ERC165BaseStorage } from './ERC165BaseStorage.sol';
/**
* @title ERC165 implementation
*/
abstract contract ERC165BaseInternal is IERC165BaseInternal {
/**
* @notice indicates whether an interface is already supported based on the interfaceId
* @param interfaceId id of interface to check
* @return bool indicating whether interface is supported
*/
function _supportsInterface(
bytes4 interfaceId
) internal view virtual returns (bool) {
return ERC165BaseStorage.layout().supportedInterfaces[interfaceId];
}
/**
* @notice sets status of interface support
* @param interfaceId id of interface to set status for
* @param status boolean indicating whether interface will be set as supported
*/
function _setSupportsInterface(
bytes4 interfaceId,
bool status
) internal virtual {
if (interfaceId == 0xffffffff) revert ERC165Base__InvalidInterfaceId();
ERC165BaseStorage.layout().supportedInterfaces[interfaceId] = status;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
library ERC165BaseStorage {
struct Layout {
mapping(bytes4 => bool) supportedInterfaces;
}
bytes32 internal constant STORAGE_SLOT =
keccak256('solidstate.contracts.storage.ERC165Base');
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly {
l.slot := slot
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC165 } from '../../../interfaces/IERC165.sol';
import { IERC165BaseInternal } from './IERC165BaseInternal.sol';
interface IERC165Base is IERC165, IERC165BaseInternal {}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC165Internal } from '../../../interfaces/IERC165Internal.sol';
interface IERC165BaseInternal is IERC165Internal {
error ERC165Base__InvalidInterfaceId();
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { Proxy } from '../../Proxy.sol';
import { IDiamondBase } from './IDiamondBase.sol';
import { DiamondBaseStorage } from './DiamondBaseStorage.sol';
/**
* @title EIP-2535 "Diamond" proxy base contract
* @dev see https://eips.ethereum.org/EIPS/eip-2535
*/
abstract contract DiamondBase is IDiamondBase, Proxy {
/**
* @inheritdoc Proxy
*/
function _getImplementation()
internal
view
virtual
override
returns (address implementation)
{
// inline storage layout retrieval uses less gas
DiamondBaseStorage.Layout storage l;
bytes32 slot = DiamondBaseStorage.STORAGE_SLOT;
assembly {
l.slot := slot
}
implementation = address(bytes20(l.facets[msg.sig]));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
/**
* @dev derived from https://github.com/mudgen/diamond-2 (MIT license)
*/
library DiamondBaseStorage {
struct Layout {
// function selector => (facet address, selector slot position)
mapping(bytes4 => bytes32) facets;
// total number of selectors registered
uint16 selectorCount;
// array of selector slots with 8 selectors per slot
mapping(uint256 => bytes32) selectorSlots;
address fallbackAddress;
}
bytes32 internal constant STORAGE_SLOT =
keccak256('solidstate.contracts.storage.DiamondBase');
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly {
l.slot := slot
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { IProxy } from '../../IProxy.sol';
interface IDiamondBase is IProxy {}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { OwnableInternal } from '../../../access/ownable/OwnableInternal.sol';
import { DiamondBase } from '../base/DiamondBase.sol';
import { DiamondBaseStorage } from '../base/DiamondBaseStorage.sol';
import { IDiamondFallback } from './IDiamondFallback.sol';
/**
* @title Fallback feature for EIP-2535 "Diamond" proxy
*/
abstract contract DiamondFallback is
IDiamondFallback,
OwnableInternal,
DiamondBase
{
/**
* @inheritdoc IDiamondFallback
*/
function getFallbackAddress()
external
view
returns (address fallbackAddress)
{
fallbackAddress = _getFallbackAddress();
}
/**
* @inheritdoc IDiamondFallback
*/
function setFallbackAddress(address fallbackAddress) external onlyOwner {
_setFallbackAddress(fallbackAddress);
}
/**
* @inheritdoc DiamondBase
* @notice query custom fallback address is no implementation is found
*/
function _getImplementation()
internal
view
virtual
override
returns (address implementation)
{
implementation = super._getImplementation();
if (implementation == address(0)) {
implementation = _getFallbackAddress();
}
}
/**
* @notice query the address of the fallback implementation
* @return fallbackAddress address of fallback implementation
*/
function _getFallbackAddress()
internal
view
virtual
returns (address fallbackAddress)
{
fallbackAddress = DiamondBaseStorage.layout().fallbackAddress;
}
/**
* @notice set the address of the fallback implementation
* @param fallbackAddress address of fallback implementation
*/
function _setFallbackAddress(address fallbackAddress) internal virtual {
DiamondBaseStorage.layout().fallbackAddress = fallbackAddress;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { IDiamondBase } from '../base/IDiamondBase.sol';
interface IDiamondFallback is IDiamondBase {
/**
* @notice query the address of the fallback implementation
* @return fallbackAddress address of fallback implementation
*/
function getFallbackAddress()
external
view
returns (address fallbackAddress);
/**
* @notice set the address of the fallback implementation
* @param fallbackAddress address of fallback implementation
*/
function setFallbackAddress(address fallbackAddress) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { ISafeOwnable } from '../../access/ownable/ISafeOwnable.sol';
import { IERC165 } from '../../interfaces/IERC165.sol';
import { IDiamondBase } from './base/IDiamondBase.sol';
import { IDiamondFallback } from './fallback/IDiamondFallback.sol';
import { IDiamondReadable } from './readable/IDiamondReadable.sol';
import { IDiamondWritable } from './writable/IDiamondWritable.sol';
interface ISolidStateDiamond is
IDiamondBase,
IDiamondFallback,
IDiamondReadable,
IDiamondWritable,
ISafeOwnable,
IERC165
{
receive() external payable;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { DiamondBaseStorage } from '../base/DiamondBaseStorage.sol';
import { IDiamondReadable } from './IDiamondReadable.sol';
/**
* @title EIP-2535 "Diamond" proxy introspection contract
* @dev derived from https://github.com/mudgen/diamond-2 (MIT license)
*/
abstract contract DiamondReadable is IDiamondReadable {
/**
* @inheritdoc IDiamondReadable
*/
function facets() external view returns (Facet[] memory diamondFacets) {
DiamondBaseStorage.Layout storage l = DiamondBaseStorage.layout();
diamondFacets = new Facet[](l.selectorCount);
uint8[] memory numFacetSelectors = new uint8[](l.selectorCount);
uint256 numFacets;
uint256 selectorIndex;
// loop through function selectors
for (uint256 slotIndex; selectorIndex < l.selectorCount; slotIndex++) {
bytes32 slot = l.selectorSlots[slotIndex];
for (
uint256 selectorSlotIndex;
selectorSlotIndex < 8;
selectorSlotIndex++
) {
selectorIndex++;
if (selectorIndex > l.selectorCount) {
break;
}
bytes4 selector = bytes4(slot << (selectorSlotIndex << 5));
address facet = address(bytes20(l.facets[selector]));
bool continueLoop;
for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) {
if (diamondFacets[facetIndex].target == facet) {
diamondFacets[facetIndex].selectors[
numFacetSelectors[facetIndex]
] = selector;
// probably will never have more than 256 functions from one facet contract
require(numFacetSelectors[facetIndex] < 255);
numFacetSelectors[facetIndex]++;
continueLoop = true;
break;
}
}
if (continueLoop) {
continue;
}
diamondFacets[numFacets].target = facet;
diamondFacets[numFacets].selectors = new bytes4[](
l.selectorCount
);
diamondFacets[numFacets].selectors[0] = selector;
numFacetSelectors[numFacets] = 1;
numFacets++;
}
}
for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) {
uint256 numSelectors = numFacetSelectors[facetIndex];
bytes4[] memory selectors = diamondFacets[facetIndex].selectors;
// setting the number of selectors
assembly {
mstore(selectors, numSelectors)
}
}
// setting the number of facets
assembly {
mstore(diamondFacets, numFacets)
}
}
/**
* @inheritdoc IDiamondReadable
*/
function facetFunctionSelectors(
address facet
) external view returns (bytes4[] memory selectors) {
DiamondBaseStorage.Layout storage l = DiamondBaseStorage.layout();
selectors = new bytes4[](l.selectorCount);
uint256 numSelectors;
uint256 selectorIndex;
// loop through function selectors
for (uint256 slotIndex; selectorIndex < l.selectorCount; slotIndex++) {
bytes32 slot = l.selectorSlots[slotIndex];
for (
uint256 selectorSlotIndex;
selectorSlotIndex < 8;
selectorSlotIndex++
) {
selectorIndex++;
if (selectorIndex > l.selectorCount) {
break;
}
bytes4 selector = bytes4(slot << (selectorSlotIndex << 5));
if (facet == address(bytes20(l.facets[selector]))) {
selectors[numSelectors] = selector;
numSelectors++;
}
}
}
// set the number of selectors in the array
assembly {
mstore(selectors, numSelectors)
}
}
/**
* @inheritdoc IDiamondReadable
*/
function facetAddresses()
external
view
returns (address[] memory addresses)
{
DiamondBaseStorage.Layout storage l = DiamondBaseStorage.layout();
addresses = new address[](l.selectorCount);
uint256 numFacets;
uint256 selectorIndex;
for (uint256 slotIndex; selectorIndex < l.selectorCount; slotIndex++) {
bytes32 slot = l.selectorSlots[slotIndex];
for (
uint256 selectorSlotIndex;
selectorSlotIndex < 8;
selectorSlotIndex++
) {
selectorIndex++;
if (selectorIndex > l.selectorCount) {
break;
}
bytes4 selector = bytes4(slot << (selectorSlotIndex << 5));
address facet = address(bytes20(l.facets[selector]));
bool continueLoop;
for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) {
if (facet == addresses[facetIndex]) {
continueLoop = true;
break;
}
}
if (continueLoop) {
continue;
}
addresses[numFacets] = facet;
numFacets++;
}
}
// set the number of facet addresses in the array
assembly {
mstore(addresses, numFacets)
}
}
/**
* @inheritdoc IDiamondReadable
*/
function facetAddress(
bytes4 selector
) external view returns (address facet) {
facet = address(bytes20(DiamondBaseStorage.layout().facets[selector]));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
/**
* @title Diamond proxy introspection interface
* @dev see https://eips.ethereum.org/EIPS/eip-2535
*/
interface IDiamondReadable {
struct Facet {
address target;
bytes4[] selectors;
}
/**
* @notice get all facets and their selectors
* @return diamondFacets array of structured facet data
*/
function facets() external view returns (Facet[] memory diamondFacets);
/**
* @notice get all selectors for given facet address
* @param facet address of facet to query
* @return selectors array of function selectors
*/
function facetFunctionSelectors(
address facet
) external view returns (bytes4[] memory selectors);
/**
* @notice get addresses of all facets used by diamond
* @return addresses array of facet addresses
*/
function facetAddresses()
external
view
returns (address[] memory addresses);
/**
* @notice get the address of the facet associated with given selector
* @param selector function selector to query
* @return facet facet address (zero address if not found)
*/
function facetAddress(
bytes4 selector
) external view returns (address facet);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { OwnableInternal } from '../../../access/ownable/OwnableInternal.sol';
import { IDiamondWritable } from './IDiamondWritable.sol';
import { DiamondWritableInternal } from './DiamondWritableInternal.sol';
/**
* @title EIP-2535 "Diamond" proxy update contract
*/
abstract contract DiamondWritable is
IDiamondWritable,
DiamondWritableInternal,
OwnableInternal
{
/**
* @inheritdoc IDiamondWritable
*/
function diamondCut(
FacetCut[] calldata facetCuts,
address target,
bytes calldata data
) external onlyOwner {
_diamondCut(facetCuts, target, data);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { AddressUtils } from '../../../utils/AddressUtils.sol';
import { DiamondBaseStorage } from '../base/DiamondBaseStorage.sol';
import { IDiamondWritableInternal } from './IDiamondWritableInternal.sol';
abstract contract DiamondWritableInternal is IDiamondWritableInternal {
using AddressUtils for address;
bytes32 private constant CLEAR_ADDRESS_MASK =
bytes32(uint256(0xffffffffffffffffffffffff));
bytes32 private constant CLEAR_SELECTOR_MASK =
bytes32(uint256(0xffffffff << 224));
/**
* @notice update functions callable on Diamond proxy
* @param facetCuts array of structured Diamond facet update data
* @param target optional recipient of initialization delegatecall
* @param data optional initialization call data
*/
function _diamondCut(
FacetCut[] memory facetCuts,
address target,
bytes memory data
) internal {
DiamondBaseStorage.Layout storage l = DiamondBaseStorage.layout();
unchecked {
uint256 originalSelectorCount = l.selectorCount;
uint256 selectorCount = originalSelectorCount;
bytes32 selectorSlot;
// Check if last selector slot is not full
if (selectorCount & 7 > 0) {
// get last selectorSlot
selectorSlot = l.selectorSlots[selectorCount >> 3];
}
for (uint256 i; i < facetCuts.length; i++) {
FacetCut memory facetCut = facetCuts[i];
FacetCutAction action = facetCut.action;
if (facetCut.selectors.length == 0)
revert DiamondWritable__SelectorNotSpecified();
if (action == FacetCutAction.ADD) {
(selectorCount, selectorSlot) = _addFacetSelectors(
l,
selectorCount,
selectorSlot,
facetCut
);
} else if (action == FacetCutAction.REPLACE) {
_replaceFacetSelectors(l, facetCut);
} else if (action == FacetCutAction.REMOVE) {
(selectorCount, selectorSlot) = _removeFacetSelectors(
l,
selectorCount,
selectorSlot,
facetCut
);
}
}
if (selectorCount != originalSelectorCount) {
l.selectorCount = uint16(selectorCount);
}
// If last selector slot is not full
if (selectorCount & 7 > 0) {
l.selectorSlots[selectorCount >> 3] = selectorSlot;
}
emit DiamondCut(facetCuts, target, data);
_initialize(target, data);
}
}
function _addFacetSelectors(
DiamondBaseStorage.Layout storage l,
uint256 selectorCount,
bytes32 selectorSlot,
FacetCut memory facetCut
) internal returns (uint256, bytes32) {
unchecked {
if (
facetCut.target != address(this) &&
!facetCut.target.isContract()
) revert DiamondWritable__TargetHasNoCode();
for (uint256 i; i < facetCut.selectors.length; i++) {
bytes4 selector = facetCut.selectors[i];
bytes32 oldFacet = l.facets[selector];
if (address(bytes20(oldFacet)) != address(0))
revert DiamondWritable__SelectorAlreadyAdded();
// add facet for selector
l.facets[selector] =
bytes20(facetCut.target) |
bytes32(selectorCount);
uint256 selectorInSlotPosition = (selectorCount & 7) << 5;
// clear selector position in slot and add selector
selectorSlot =
(selectorSlot &
~(CLEAR_SELECTOR_MASK >> selectorInSlotPosition)) |
(bytes32(selector) >> selectorInSlotPosition);
// if slot is full then write it to storage
if (selectorInSlotPosition == 224) {
l.selectorSlots[selectorCount >> 3] = selectorSlot;
selectorSlot = 0;
}
selectorCount++;
}
return (selectorCount, selectorSlot);
}
}
function _removeFacetSelectors(
DiamondBaseStorage.Layout storage l,
uint256 selectorCount,
bytes32 selectorSlot,
FacetCut memory facetCut
) internal returns (uint256, bytes32) {
unchecked {
if (facetCut.target != address(0))
revert DiamondWritable__RemoveTargetNotZeroAddress();
uint256 selectorSlotCount = selectorCount >> 3;
uint256 selectorInSlotIndex = selectorCount & 7;
for (uint256 i; i < facetCut.selectors.length; i++) {
bytes4 selector = facetCut.selectors[i];
bytes32 oldFacet = l.facets[selector];
if (address(bytes20(oldFacet)) == address(0))
revert DiamondWritable__SelectorNotFound();
if (address(bytes20(oldFacet)) == address(this))
revert DiamondWritable__SelectorIsImmutable();
if (selectorSlot == 0) {
selectorSlotCount--;
selectorSlot = l.selectorSlots[selectorSlotCount];
selectorInSlotIndex = 7;
} else {
selectorInSlotIndex--;
}
bytes4 lastSelector;
uint256 oldSelectorsSlotCount;
uint256 oldSelectorInSlotPosition;
// adding a block here prevents stack too deep error
{
// replace selector with last selector in l.facets
lastSelector = bytes4(
selectorSlot << (selectorInSlotIndex << 5)
);
if (lastSelector != selector) {
// update last selector slot position info
l.facets[lastSelector] =
(oldFacet & CLEAR_ADDRESS_MASK) |
bytes20(l.facets[lastSelector]);
}
delete l.facets[selector];
uint256 oldSelectorCount = uint16(uint256(oldFacet));
oldSelectorsSlotCount = oldSelectorCount >> 3;
oldSelectorInSlotPosition = (oldSelectorCount & 7) << 5;
}
if (oldSelectorsSlotCount != selectorSlotCount) {
bytes32 oldSelectorSlot = l.selectorSlots[
oldSelectorsSlotCount
];
// clears the selector we are deleting and puts the last selector in its place.
oldSelectorSlot =
(oldSelectorSlot &
~(CLEAR_SELECTOR_MASK >>
oldSelectorInSlotPosition)) |
(bytes32(lastSelector) >> oldSelectorInSlotPosition);
// update storage with the modified slot
l.selectorSlots[oldSelectorsSlotCount] = oldSelectorSlot;
} else {
// clears the selector we are deleting and puts the last selector in its place.
selectorSlot =
(selectorSlot &
~(CLEAR_SELECTOR_MASK >>
oldSelectorInSlotPosition)) |
(bytes32(lastSelector) >> oldSelectorInSlotPosition);
}
if (selectorInSlotIndex == 0) {
delete l.selectorSlots[selectorSlotCount];
selectorSlot = 0;
}
}
selectorCount = (selectorSlotCount << 3) | selectorInSlotIndex;
return (selectorCount, selectorSlot);
}
}
function _replaceFacetSelectors(
DiamondBaseStorage.Layout storage l,
FacetCut memory facetCut
) internal {
unchecked {
if (!facetCut.target.isContract())
revert DiamondWritable__TargetHasNoCode();
for (uint256 i; i < facetCut.selectors.length; i++) {
bytes4 selector = facetCut.selectors[i];
bytes32 oldFacet = l.facets[selector];
address oldFacetAddress = address(bytes20(oldFacet));
if (oldFacetAddress == address(0))
revert DiamondWritable__SelectorNotFound();
if (oldFacetAddress == address(this))
revert DiamondWritable__SelectorIsImmutable();
if (oldFacetAddress == facetCut.target)
revert DiamondWritable__ReplaceTargetIsIdentical();
// replace old facet address
l.facets[selector] =
(oldFacet & CLEAR_ADDRESS_MASK) |
bytes20(facetCut.target);
}
}
}
function _initialize(address target, bytes memory data) private {
if ((target == address(0)) != (data.length == 0))
revert DiamondWritable__InvalidInitializationParameters();
if (target != address(0)) {
if (target != address(this)) {
if (!target.isContract())
revert DiamondWritable__TargetHasNoCode();
}
(bool success, ) = target.delegatecall(data);
if (!success) {
assembly {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { IDiamondWritableInternal } from './IDiamondWritableInternal.sol';
/**
* @title Diamond proxy upgrade interface
* @dev see https://eips.ethereum.org/EIPS/eip-2535
*/
interface IDiamondWritable is IDiamondWritableInternal {
/**
* @notice update diamond facets and optionally execute arbitrary initialization function
* @param facetCuts array of structured Diamond facet update data
* @param target optional target of initialization delegatecall
* @param data optional initialization function call data
*/
function diamondCut(
FacetCut[] calldata facetCuts,
address target,
bytes calldata data
) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
interface IDiamondWritableInternal {
enum FacetCutAction {
ADD,
REPLACE,
REMOVE
}
event DiamondCut(FacetCut[] facetCuts, address target, bytes data);
error DiamondWritable__InvalidInitializationParameters();
error DiamondWritable__RemoveTargetNotZeroAddress();
error DiamondWritable__ReplaceTargetIsIdentical();
error DiamondWritable__SelectorAlreadyAdded();
error DiamondWritable__SelectorIsImmutable();
error DiamondWritable__SelectorNotFound();
error DiamondWritable__SelectorNotSpecified();
error DiamondWritable__TargetHasNoCode();
struct FacetCut {
address target;
FacetCutAction action;
bytes4[] selectors;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
interface IProxy {
error Proxy__ImplementationIsNotContract();
fallback() external payable;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { AddressUtils } from '../utils/AddressUtils.sol';
import { IProxy } from './IProxy.sol';
/**
* @title Base proxy contract
*/
abstract contract Proxy is IProxy {
using AddressUtils for address;
/**
* @notice delegate all calls to implementation contract
* @dev reverts if implementation address contains no code, for compatibility with metamorphic contracts
* @dev memory location in use by assembly may be unsafe in other contexts
*/
fallback() external payable virtual {
address implementation = _getImplementation();
if (!implementation.isContract())
revert Proxy__ImplementationIsNotContract();
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(
gas(),
implementation,
0,
calldatasize(),
0,
0
)
returndatacopy(0, 0, returndatasize())
switch result
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/**
* @notice get logic implementation address
* @return implementation address
*/
function _getImplementation() internal virtual returns (address);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
library ERC2981Storage {
struct Layout {
// token id -> royalty (denominated in basis points)
mapping(uint256 => uint16) royaltiesBPS;
uint16 defaultRoyaltyBPS;
// token id -> receiver address
mapping(uint256 => address) royaltyReceivers;
address defaultRoyaltyReceiver;
}
bytes32 internal constant STORAGE_SLOT =
keccak256('solidstate.contracts.storage.ERC2981');
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly {
l.slot := slot
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
interface IERC721Enumerable {
/**
* @notice get total token supply
* @return total supply
*/
function totalSupply() external view returns (uint256);
/**
* @notice get token of given owner at given internal storage index
* @param owner token holder to query
* @param index position in owner's token list to query
* @return tokenId id of retrieved token
*/
function tokenOfOwnerByIndex(
address owner,
uint256 index
) external view returns (uint256 tokenId);
/**
* @notice get token at given internal storage index
* @param index position in global token list to query
* @return tokenId id of retrieved token
*/
function tokenByIndex(
uint256 index
) external view returns (uint256 tokenId);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
library ERC721MetadataStorage {
bytes32 internal constant STORAGE_SLOT =
keccak256('solidstate.contracts.storage.ERC721Metadata');
struct Layout {
string name;
string symbol;
string baseURI;
mapping(uint256 => string) tokenURIs;
}
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly {
l.slot := slot
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import { UintUtils } from './UintUtils.sol';
library AddressUtils {
using UintUtils for uint256;
error AddressUtils__InsufficientBalance();
error AddressUtils__NotContract();
error AddressUtils__SendValueFailed();
function toString(address account) internal pure returns (string memory) {
return uint256(uint160(account)).toHexString(20);
}
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
function sendValue(address payable account, uint256 amount) internal {
(bool success, ) = account.call{ value: amount }('');
if (!success) revert AddressUtils__SendValueFailed();
}
function functionCall(
address target,
bytes memory data
) internal returns (bytes memory) {
return
functionCall(target, data, 'AddressUtils: failed low-level call');
}
function functionCall(
address target,
bytes memory data,
string memory error
) internal returns (bytes memory) {
return _functionCallWithValue(target, data, 0, error);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return
functionCallWithValue(
target,
data,
value,
'AddressUtils: failed low-level call with value'
);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory error
) internal returns (bytes memory) {
if (value > address(this).balance)
revert AddressUtils__InsufficientBalance();
return _functionCallWithValue(target, data, value, error);
}
/**
* @notice execute arbitrary external call with limited gas usage and amount of copied return data
* @dev derived from https://github.com/nomad-xyz/ExcessivelySafeCall (MIT License)
* @param target recipient of call
* @param gasAmount gas allowance for call
* @param value native token value to include in call
* @param maxCopy maximum number of bytes to copy from return data
* @param data encoded call data
* @return success whether call is successful
* @return returnData copied return data
*/
function excessivelySafeCall(
address target,
uint256 gasAmount,
uint256 value,
uint16 maxCopy,
bytes memory data
) internal returns (bool success, bytes memory returnData) {
returnData = new bytes(maxCopy);
assembly {
// execute external call via assembly to avoid automatic copying of return data
success := call(
gasAmount,
target,
value,
add(data, 0x20),
mload(data),
0,
0
)
// determine whether to limit amount of data to copy
let toCopy := returndatasize()
if gt(toCopy, maxCopy) {
toCopy := maxCopy
}
// store the length of the copied bytes
mstore(returnData, toCopy)
// copy the bytes from returndata[0:toCopy]
returndatacopy(add(returnData, 0x20), 0, toCopy)
}
}
function _functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory error
) private returns (bytes memory) {
if (!isContract(target)) revert AddressUtils__NotContract();
(bool success, bytes memory returnData) = target.call{ value: value }(
data
);
if (success) {
return returnData;
} else if (returnData.length > 0) {
assembly {
let returnData_size := mload(returnData)
revert(add(32, returnData), returnData_size)
}
} else {
revert(error);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
/**
* @title utility functions for uint256 operations
* @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license)
*/
library UintUtils {
error UintUtils__InsufficientHexLength();
bytes16 private constant HEX_SYMBOLS = '0123456789abcdef';
function add(uint256 a, int256 b) internal pure returns (uint256) {
return b < 0 ? sub(a, -b) : a + uint256(b);
}
function sub(uint256 a, int256 b) internal pure returns (uint256) {
return b < 0 ? add(a, -b) : a - uint256(b);
}
function toString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return '0';
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return '0x00';
}
uint256 length = 0;
for (uint256 temp = value; temp != 0; temp >>= 8) {
unchecked {
length++;
}
}
return toHexString(value, 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';
unchecked {
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
}
if (value != 0) revert UintUtils__InsufficientHexLength();
return string(buffer);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;
import { IOperatorFilterRegistry } from "operator-filter-registry/src/IOperatorFilterRegistry.sol";
/**
* @dev copied from operator-filter-registry/src/upgradeable/OperatorFiltererUpgradeable.sol
* @dev some of the control flow was removed to remove unnecessary branches
*/
contract DiamondOperatorFilter {
IOperatorFilterRegistry constant OPERATOR_FILTER_REGISTRY =
IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);
/// @dev The upgradeable initialize function that should be called when the contract is being upgraded.
function __OperatorFilterer_init(address subscriptionOrRegistrantToCopy) internal {
if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
if (!OPERATOR_FILTER_REGISTRY.isRegistered(address(this))) {
OPERATOR_FILTER_REGISTRY.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;
import { OwnableInternal } from "@solidstate/contracts/access/ownable/OwnableInternal.sol";
import { IDiamondWritable } from "@solidstate/contracts/proxy/diamond/writable/IDiamondWritable.sol";
import { DiamondWritableInternal } from "@solidstate/contracts/proxy/diamond/writable/DiamondWritableInternal.sol";
import { DiamondWritableRevokableStorage } from "./DiamondWritableRevokableStorage.sol";
/**
* @title EIP-2535 "Diamond" proxy update contract with built in revokability
*/
abstract contract DiamondWritableRevokable is IDiamondWritable, DiamondWritableInternal, OwnableInternal {
error UpgradeabilityRevoked();
/**
* @inheritdoc IDiamondWritable
* @dev also checks to ensure upgradeability has not been revoked
*/
function diamondCut(FacetCut[] calldata facetCuts, address target, bytes calldata data) external onlyOwner {
if (DiamondWritableRevokableStorage.layout().isUpgradeabiltyRevoked) revert UpgradeabilityRevoked();
_diamondCut(facetCuts, target, data);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;
library DiamondWritableRevokableStorage {
bytes32 internal constant STORAGE_SLOT = keccak256("keepers.contracts.storage.diamond.writable.revokable");
struct Layout {
bool isUpgradeabiltyRevoked;
}
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly {
l.slot := slot
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;
library KeepersERC721Storage {
bytes32 internal constant STORAGE_SLOT = keccak256("keepers.contracts.storage.keepers.erc721.storage");
// @dev struct which describes a commit
// @dev use uint128 for tight variable packing
struct MintCommit {
uint128 numNFTs;
uint128 commitBlock;
}
struct Layout {
// @dev keeps track of available tokens for fisher yates shuffle
mapping(uint256 => uint256) availableTokens;
// @dev a mapping of user address to their pending commit
mapping(address => MintCommit) pendingCommits;
// Mint parameters
mapping(address => uint256) mintCountPerAddress;
// the following variables will be tightly packed
uint16 numAvailableTokens;
uint16 maxPerAddress;
uint32 saleStartTimestamp; // max val in 2106
uint32 saleCompleteTimestamp;
uint160 numPendingCommitNFTs;
address withdrawAddress;
address vaultAddress;
uint256 maxMintsForSalesTier; // when kept at 0 it is ignored
}
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly {
l.slot := slot
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;
import "@openzeppelin/contracts/utils/Counters.sol";
library TermsStorage {
using Counters for Counters.Counter;
bytes32 internal constant STORAGE_SLOT = keccak256("keepers.contracts.storage.terms");
struct Layout {
mapping(uint256 => string) termsParts; // mapping of term part number to term part string
Counters.Counter termsVersion;
}
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly {
l.slot := slot
}
}
}
// SPDX-License-Identifier: MIT
/*
..';;::::::;;;::::;,'.
.,;;;;;,'.... ....';;;;;;'.
';;;,.. .';;:,.
.,;,. .';;'
.,;. ... .,;.
.;. .;;.
',. ';.
'' .,.
.' .,
.'. ..... ..
.. .;;,,,;,,;,. ..
.. ;c' .;c' ..
.. ;c. 'c. .
. .',;dl. .;dl,,'. ..
. ,:;,..:xc:,. .;:od,.',;;. .
. .:c. ,c:cc. ,l:::. 'c' .
. ,l. .,:docldl;' ,c.
,c. ';ddldxl,. ,c.
.::. '::lo' .;oc:;. .c'
.,:,...;dc::. '::do...';:.
':clkd,. .cxdlc;.
.,;,,,od:;. .,;ldc,,;;'
,c,. .:c:c, .:::c, .:c.
'l. ,;cxc;:do:;. ;c.
,c. .;xkxOkl. ,c.
.c, .;:lx:';od::' .c:
.:;. .lc;c, .:::l; .,c;
.,;;:do;,. .;cxl;;;,.
';;;:do;'. .,cxl;;;,.
.:;. 'oc::' .;::o:. .,c;.
.l' .::ld;.,ld::, .c;
;c. .:xkxOko' ,c.
.l' .,;cxc;cdo:;. .c:
'c;. .cc;c; .c::l, 'c;.
.';;,;od:,. .,;lxc,;;;.
.'l: .o:..
'l' .::.
.:;'......,:;.
.,;,,,,,'.
.. ...
.''. .''..
',,'. ..,,,. ......
..,;:c;''... .....'.. ...':c'
.;:;;'. ................ .:Kx.
.:lc,. ..';:cokd'
;xo. ..,:ldkkOOxo,
;0l ...',:oxO0KXK0xl;.
;0x' ................''',;:lodkOKXNWNX0ko:'.
,oxdoccclodxxxkkkOO00KKXXXNWWWWWNK0xoc;..
.':lloodxkO00KKKKKK0OOOkxdoc:;'..
................
*/
pragma solidity 0.8.18;
import { SolidStateDiamondRevokableUpgradability } from "./SolidStateDiamondRevokableUpgradability.sol";
import { AccessControlInternal } from "@solidstate/contracts/access/access_control/AccessControlInternal.sol";
import { IERC721 } from "@solidstate/contracts/interfaces/IERC721.sol";
import { IERC165 } from "@solidstate/contracts/interfaces/IERC165.sol";
import { IERC2981 } from "@solidstate/contracts/interfaces/IERC2981.sol";
import { IERC721Enumerable } from "@solidstate/contracts/token/ERC721/enumerable/IERC721Enumerable.sol";
import { ERC2981Storage } from "@solidstate/contracts/token/common/ERC2981/ERC2981Storage.sol";
import { KeepersERC721Storage } from "./facets/KeepersERC721/KeepersERC721Storage.sol";
import { ERC721MetadataStorage } from "@solidstate/contracts/token/ERC721/metadata/ERC721MetadataStorage.sol";
import { TermsStorage } from "./facets/Terms/TermsStorage.sol";
import { ConstantsLib } from "./library/ConstantsLib.sol";
import { DiamondWritableInternal } from "@solidstate/contracts/proxy/diamond/writable/DiamondWritableInternal.sol";
import { CANONICAL_CORI_SUBSCRIPTION } from "operator-filter-registry/src/lib/Constants.sol";
import { DiamondOperatorFilter } from "./DiamondOperatorFilter.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
/**
* @title KeepersDiamond "Diamond" proxy implementation based on SolidState Diamond Reference
*/
contract KeepersDiamond is SolidStateDiamondRevokableUpgradability, AccessControlInternal, DiamondOperatorFilter {
using Counters for Counters.Counter;
constructor(string memory name_, string memory symbol_, string memory baseURI_) {
// ERC165 - note: other interfaces are set in SolidStateDiamondRevokableUpgradability
_setSupportsInterface(type(IERC721).interfaceId, true);
_setSupportsInterface(type(IERC721Enumerable).interfaceId, true);
_setSupportsInterface(type(IERC2981).interfaceId, true);
// AccessControl
_grantRole(ConstantsLib.KEEPERS_TERMS_OPERATOR, msg.sender);
_grantRole(ConstantsLib.KEEPERS_LICENSE_OPERATOR, msg.sender);
// ERC2981
ERC2981Storage.layout().defaultRoyaltyBPS = 700; // 7%
ERC2981Storage.layout().defaultRoyaltyReceiver = msg.sender;
// mint parameters
KeepersERC721Storage.layout().maxPerAddress = 10;
KeepersERC721Storage.layout().numAvailableTokens = uint16(ConstantsLib.MAX_TICKETS);
// Metadata
ERC721MetadataStorage.layout().name = name_;
ERC721MetadataStorage.layout().symbol = symbol_;
ERC721MetadataStorage.layout().baseURI = baseURI_;
// Terms version
TermsStorage.layout().termsVersion.increment(); // Set terms to V1 by default
// Set up Operator Filter Registry
__OperatorFilterer_init(CANONICAL_CORI_SUBSCRIPTION);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;
library ConstantsLib {
// mint params
uint256 internal constant MINT_PRICE = 0.5 ether;
uint256 internal constant ALLOWLSIT_MINT_PRICE = 0.25 ether;
uint256 internal constant MAX_TICKETS = 20000;
uint256 internal constant MIN_COMMITMENT_BLOCKS = 4;
uint256 internal constant MAX_COMMITMENT_BLOCKS = 100;
uint256 internal constant SPECIAL_TICKETS_COUNT = 10;
uint256 internal constant SPECIAL_TICKET_MINT_ODDS = 1000;
// access control
bytes32 internal constant KEEPERS_TERMS_OPERATOR = keccak256("KEEPERS_TERMS_OPERATOR");
bytes32 internal constant KEEPERS_LICENSE_OPERATOR = keccak256("KEEPERS_LICENSE_OPERATOR");
bytes32 internal constant KEEPERS_MINT_OPERATOR = keccak256("KEEPERS_MINT_OPERATOR");
// token states
uint256 internal constant TOKEN_STATE_UNCONVERTED = 0;
uint256 internal constant TOKEN_STATE_USER_CONVERTED = 1;
uint256 internal constant TOKEN_STATE_ADMIN_CONVERTED = 2;
// room naming
uint256 internal constant NO_ROOM_NAMING_RIGHTS = 0;
uint256 internal constant MAX_ROOM_NAME_LENGTH = 40;
uint256 internal constant MIN_ROOM_ID = 1;
// traits
uint256 internal constant MIN_TRAIT_ID = 1;
uint256 internal constant MAX_TRAIT_ID = 242;
uint256 internal constant BLACK_BODY_ID = 1;
uint256 internal constant GLASS_BODY_ID = 10;
uint256 internal constant MAX_NON_SPECIAL_BODY_ID = 9;
// rarities
// bitmap for ultra rare traits 0000001111100000000000111001101110000000000000000100010000001111100000000000000000000000000000000000000000011100000000000000000000100000000000000000000011101010001000000000000000010000000000000000000000000000000000000000000000000000000000001110000000000000
uint256 internal constant RARITY_ULTRARARE =
1752737185771013737644568005558321144114900560241663114319964015025618739200;
// bitmap for rare traits 0011100000000000000111000000000000000000000000000001100111110000000000000000000000000000000000001000000111100000000000000000010010000001000000000000111100010101110000000000001000100100000000000000000000000000000000000101000001000000111111110000000000000000
uint256 internal constant RARITY_RARE =
25329712769560669564647724112116350647150594218874243813999282872231118438400;
// bitmap for uncommon traits 0000010000000100010000000110010000000000000000001010000000000000000000000000000000000000000001010001111000000000000000000000000001010100001111111111000000000000000100001000110111001000000000000000000000000000000000001010111100110111000000000000000000000000
uint256 internal constant RARITY_UNCOMMON =
1816763190353297512045086567763279289688669177570068147176572168506815021056;
// bitmap for common traits 0100000000011011101000000000000001111111111111110000001000000000011111111111111111111111111110100110000000000011111111111111101100001010110000000000000000000000000011110111000000000011111111111111111111111111111111110000000010001000000000000000000000000000
uint256 internal constant RARITY_COMMON =
28996831472973116897448113818906002845680828376134306944232972948193012613120;
// misc
uint256 internal constant EMPTY_BITMAP = 0;
string internal constant WHERE_TO_FIND_TERMS =
"Terms and conditions can be found by calling getTerms() on the Keepers contract.";
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;
import { IOwnable, Ownable, OwnableInternal } from "@solidstate/contracts/access/ownable/Ownable.sol";
import { ISafeOwnable, SafeOwnable } from "@solidstate/contracts/access/ownable/SafeOwnable.sol";
import { IERC165 } from "@solidstate/contracts/interfaces/IERC165.sol";
import { IERC173 } from "@solidstate/contracts/interfaces/IERC173.sol";
import { ERC165Base, ERC165BaseStorage } from "@solidstate/contracts/introspection/ERC165/base/ERC165Base.sol";
import { DiamondBase } from "@solidstate/contracts/proxy/diamond/base/DiamondBase.sol";
import { DiamondFallback, IDiamondFallback } from "@solidstate/contracts/proxy/diamond/fallback/DiamondFallback.sol";
import { DiamondReadable, IDiamondReadable } from "@solidstate/contracts/proxy/diamond/readable/DiamondReadable.sol";
import { IDiamondWritable } from "@solidstate/contracts/proxy/diamond/writable/DiamondWritable.sol";
import { DiamondWritableRevokable } from "./facets/DiamondWritableRevokable/DiamondWritableRevokable.sol";
import { ISolidStateDiamond } from "@solidstate/contracts/proxy/diamond/ISolidStateDiamond.sol";
/**
* @title SolidState "Diamond" proxy reference implementation with DiamondWritableRevokable
*/
abstract contract SolidStateDiamondRevokableUpgradability is
ISolidStateDiamond,
DiamondBase,
DiamondFallback,
DiamondReadable,
DiamondWritableRevokable,
SafeOwnable,
ERC165Base
{
constructor() {
bytes4[] memory selectors = new bytes4[](12);
uint256 selectorIndex;
// register DiamondFallback
selectors[selectorIndex++] = IDiamondFallback.getFallbackAddress.selector;
selectors[selectorIndex++] = IDiamondFallback.setFallbackAddress.selector;
_setSupportsInterface(type(IDiamondFallback).interfaceId, true);
// register DiamondWritable
selectors[selectorIndex++] = IDiamondWritable.diamondCut.selector;
_setSupportsInterface(type(IDiamondWritable).interfaceId, true);
// register DiamondReadable
selectors[selectorIndex++] = IDiamondReadable.facets.selector;
selectors[selectorIndex++] = IDiamondReadable.facetFunctionSelectors.selector;
selectors[selectorIndex++] = IDiamondReadable.facetAddresses.selector;
selectors[selectorIndex++] = IDiamondReadable.facetAddress.selector;
_setSupportsInterface(type(IDiamondReadable).interfaceId, true);
// register ERC165
selectors[selectorIndex++] = IERC165.supportsInterface.selector;
_setSupportsInterface(type(IERC165).interfaceId, true);
// register SafeOwnable
selectors[selectorIndex++] = Ownable.owner.selector;
selectors[selectorIndex++] = SafeOwnable.nomineeOwner.selector;
selectors[selectorIndex++] = Ownable.transferOwnership.selector;
selectors[selectorIndex++] = SafeOwnable.acceptOwnership.selector;
_setSupportsInterface(type(IERC173).interfaceId, true);
// diamond cut
FacetCut[] memory facetCuts = new FacetCut[](1);
facetCuts[0] = FacetCut({ target: address(this), action: FacetCutAction.ADD, selectors: selectors });
_diamondCut(facetCuts, address(0), "");
// set owner
_setOwner(msg.sender);
}
receive() external payable {}
function _transferOwnership(address account) internal virtual override(OwnableInternal, SafeOwnable) {
super._transferOwnership(account);
}
/**
* @inheritdoc DiamondFallback
*/
function _getImplementation()
internal
view
override(DiamondBase, DiamondFallback)
returns (address implementation)
{
implementation = super._getImplementation();
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
interface IOperatorFilterRegistry {
/**
* @notice Returns true if operator is not filtered for a given token, either by address or codeHash. Also returns
* true if supplied registrant address is not registered.
*/
function isOperatorAllowed(address registrant, address operator) external view returns (bool);
/**
* @notice Registers an address with the registry. May be called by address itself or by EIP-173 owner.
*/
function register(address registrant) external;
/**
* @notice Registers an address with the registry and "subscribes" to another address's filtered operators and codeHashes.
*/
function registerAndSubscribe(address registrant, address subscription) external;
/**
* @notice Registers an address with the registry and copies the filtered operators and codeHashes from another
* address without subscribing.
*/
function registerAndCopyEntries(address registrant, address registrantToCopy) external;
/**
* @notice Unregisters an address with the registry and removes its subscription. May be called by address itself or by EIP-173 owner.
* Note that this does not remove any filtered addresses or codeHashes.
* Also note that any subscriptions to this registrant will still be active and follow the existing filtered addresses and codehashes.
*/
function unregister(address addr) external;
/**
* @notice Update an operator address for a registered address - when filtered is true, the operator is filtered.
*/
function updateOperator(address registrant, address operator, bool filtered) external;
/**
* @notice Update multiple operators for a registered address - when filtered is true, the operators will be filtered. Reverts on duplicates.
*/
function updateOperators(address registrant, address[] calldata operators, bool filtered) external;
/**
* @notice Update a codeHash for a registered address - when filtered is true, the codeHash is filtered.
*/
function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external;
/**
* @notice Update multiple codeHashes for a registered address - when filtered is true, the codeHashes will be filtered. Reverts on duplicates.
*/
function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external;
/**
* @notice Subscribe an address to another registrant's filtered operators and codeHashes. Will remove previous
* subscription if present.
* Note that accounts with subscriptions may go on to subscribe to other accounts - in this case,
* subscriptions will not be forwarded. Instead the former subscription's existing entries will still be
* used.
*/
function subscribe(address registrant, address registrantToSubscribe) external;
/**
* @notice Unsubscribe an address from its current subscribed registrant, and optionally copy its filtered operators and codeHashes.
*/
function unsubscribe(address registrant, bool copyExistingEntries) external;
/**
* @notice Get the subscription address of a given registrant, if any.
*/
function subscriptionOf(address addr) external returns (address registrant);
/**
* @notice Get the set of addresses subscribed to a given registrant.
* Note that order is not guaranteed as updates are made.
*/
function subscribers(address registrant) external returns (address[] memory);
/**
* @notice Get the subscriber at a given index in the set of addresses subscribed to a given registrant.
* Note that order is not guaranteed as updates are made.
*/
function subscriberAt(address registrant, uint256 index) external returns (address);
/**
* @notice Copy filtered operators and codeHashes from a different registrantToCopy to addr.
*/
function copyEntriesOf(address registrant, address registrantToCopy) external;
/**
* @notice Returns true if operator is filtered by a given address or its subscription.
*/
function isOperatorFiltered(address registrant, address operator) external returns (bool);
/**
* @notice Returns true if the hash of an address's code is filtered by a given address or its subscription.
*/
function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
/**
* @notice Returns true if a codeHash is filtered by a given address or its subscription.
*/
function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
/**
* @notice Returns a list of filtered operators for a given address or its subscription.
*/
function filteredOperators(address addr) external returns (address[] memory);
/**
* @notice Returns the set of filtered codeHashes for a given address or its subscription.
* Note that order is not guaranteed as updates are made.
*/
function filteredCodeHashes(address addr) external returns (bytes32[] memory);
/**
* @notice Returns the filtered operator at the given index of the set of filtered operators for a given address or
* its subscription.
* Note that order is not guaranteed as updates are made.
*/
function filteredOperatorAt(address registrant, uint256 index) external returns (address);
/**
* @notice Returns the filtered codeHash at the given index of the list of filtered codeHashes for a given address or
* its subscription.
* Note that order is not guaranteed as updates are made.
*/
function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
/**
* @notice Returns true if an address has registered
*/
function isRegistered(address addr) external returns (bool);
/**
* @dev Convenience method to compute the code hash of an arbitrary contract
*/
function codeHashOf(address addr) external returns (bytes32);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
address constant CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS = 0x000000000000AAeB6D7670E522A718067333cd4E;
address constant CANONICAL_CORI_SUBSCRIPTION = 0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6;