Transaction Hash:
Block:
15871317 at Oct-31-2022 11:27:23 PM +UTC
Transaction Fee:
0.00071033524002447 ETH
$1.72
Gas Used:
51,195 Gas / 13.875090146 Gwei
Emitted Events:
110 |
Diamond.0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31( 0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31, 0x0000000000000000000000003df03cd0fcffdc255c25a9905656065bff5f01aa, 0x000000000000000000000000f849de01b080adc3a814fabe1e2087475cf2e354, 0000000000000000000000000000000000000000000000000000000000000001 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x3dF03cd0...BFF5f01aA |
0.033656154349322263 Eth
Nonce: 15
|
0.032945819109297793 Eth
Nonce: 16
| 0.00071033524002447 | ||
0x535B918F...0592990A3
Miner
| (Fee Recipient: 0x535...0A3) | 7.109992967377910995 Eth | 7.110069759877910995 Eth | 0.0000767925 | |
0x9f0F6E4e...bEdCC7C1D |
Execution Trace
Diamond.a22cb465( )

-
NftCommonFacet.setApprovalForAll( _operator=0xF849de01B080aDC3A814FaBE1E2087475cF2E354, _approved=True )
File 1 of 2: Diamond
File 2 of 2: NftCommonFacet
// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; /******************************************************************************\\ * Authors: Nick Mudge (https://twitter.com/mudgen) * * Implementation of a diamond. /******************************************************************************/ import {LibDiamond} from "./libraries/LibDiamond.sol"; import {DiamondCutFacet} from "./facets/DiamondCutFacet.sol"; import {DiamondLoupeFacet} from "./facets/DiamondLoupeFacet.sol"; import {OwnershipFacet} from "./facets/OwnershipFacet.sol"; contract Diamond { constructor(address _contractOwner) { LibDiamond.setContractOwner(_contractOwner); LibDiamond.addDiamondFunctions( address(new DiamondCutFacet()), address(new DiamondLoupeFacet()), address(new OwnershipFacet()) ); } // Find facet for function that is called and execute the // function if a facet is found and return any value. fallback() external payable { LibDiamond.DiamondStorage storage ds; bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION; assembly { ds.slot := position } address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress; require(facet != address(0), "Diamond: Function does not exist"); assembly { calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; /******************************************************************************\\ * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen) * EIP-2535 Diamond Standard: https://eips.ethereum.org/EIPS/eip-2535 /******************************************************************************/ import {IDiamondCut} from "../interfaces/IDiamondCut.sol"; import {LibDiamond} from "../libraries/LibDiamond.sol"; contract DiamondCutFacet is IDiamondCut { /// @notice Add/replace/remove any number of functions and optionally execute /// a function with delegatecall /// @param _diamondCut Contains the facet addresses and function selectors /// @param _init The address of the contract or facet to execute _calldata /// @param _calldata A function call, including function selector and arguments /// _calldata is executed with delegatecall on _init function diamondCut( FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata ) external override { LibDiamond.enforceIsContractOwner(); LibDiamond.diamondCut(_diamondCut, _init, _calldata); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; /******************************************************************************\\ * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen) * EIP-2535 Diamond Standard: https://eips.ethereum.org/EIPS/eip-2535 /******************************************************************************/ import {IDiamondCut} from "../interfaces/IDiamondCut.sol"; import {IDiamondLoupe} from "../interfaces/IDiamondLoupe.sol"; import {IERC165} from "../interfaces/IERC165.sol"; import {IOwnable} from "../interfaces/IOwnable.sol"; import {LibMeta} from "./LibMeta.sol"; library LibDiamond { bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage"); struct FacetAddressAndPosition { address facetAddress; uint16 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array } struct FacetFunctionSelectors { bytes4[] functionSelectors; uint16 facetAddressPosition; // position of facetAddress in facetAddresses array } struct DiamondStorage { // maps function selector to the facet address and // the position of the selector in the facetFunctionSelectors.selectors array mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition; // maps facet addresses to function selectors mapping(address => FacetFunctionSelectors) facetFunctionSelectors; // facet addresses address[] facetAddresses; // Used to query if a contract implements an interface. // Used to implement ERC-165. mapping(bytes4 => bool) supportedInterfaces; // owner of the contract address contractOwner; } function diamondStorage() internal pure returns (DiamondStorage storage ds) { bytes32 position = DIAMOND_STORAGE_POSITION; assembly { ds.slot := position } } event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); function setContractOwner(address _newOwner) internal { DiamondStorage storage ds = diamondStorage(); address previousOwner = ds.contractOwner; ds.contractOwner = _newOwner; emit OwnershipTransferred(previousOwner, _newOwner); } function contractOwner() internal view returns (address contractOwner_) { contractOwner_ = diamondStorage().contractOwner; } function enforceIsContractOwner() internal view { require(LibMeta.msgSender() == diamondStorage().contractOwner, "LibDiamond: Must be contract owner"); } event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata); function addDiamondFunctions( address _diamondCutFacet, address _diamondLoupeFacet, address _ownershipFacet ) internal { IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](3); bytes4[] memory functionSelectors = new bytes4[](1); functionSelectors[0] = IDiamondCut.diamondCut.selector; cut[0] = IDiamondCut.FacetCut({facetAddress: _diamondCutFacet, action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors}); functionSelectors = new bytes4[](5); functionSelectors[0] = IDiamondLoupe.facets.selector; functionSelectors[1] = IDiamondLoupe.facetFunctionSelectors.selector; functionSelectors[2] = IDiamondLoupe.facetAddresses.selector; functionSelectors[3] = IDiamondLoupe.facetAddress.selector; functionSelectors[4] = IERC165.supportsInterface.selector; cut[1] = IDiamondCut.FacetCut({ facetAddress: _diamondLoupeFacet, action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors }); functionSelectors = new bytes4[](2); functionSelectors[0] = IOwnable.transferOwnership.selector; functionSelectors[1] = IOwnable.owner.selector; cut[2] = IDiamondCut.FacetCut({facetAddress: _ownershipFacet, action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors}); diamondCut(cut, address(0), ""); } // Internal function version of diamondCut function diamondCut( IDiamondCut.FacetCut[] memory _diamondCut, address _init, bytes memory _calldata ) internal { for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) { IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action; if (action == IDiamondCut.FacetCutAction.Add) { addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else if (action == IDiamondCut.FacetCutAction.Replace) { replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else if (action == IDiamondCut.FacetCutAction.Remove) { removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else { revert("LibDiamondCut: Incorrect FacetCutAction"); } } emit DiamondCut(_diamondCut, _init, _calldata); initializeDiamondCut(_init, _calldata); } function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); // uint16 selectorCount = uint16(diamondStorage().selectors.length); require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)"); uint16 selectorPosition = uint16(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length); // add new facet address if it does not exist if (selectorPosition == 0) { enforceHasContractCode(_facetAddress, "LibDiamondCut: New facet has no code"); ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = uint16(ds.facetAddresses.length); ds.facetAddresses.push(_facetAddress); } for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; require(oldFacetAddress == address(0), "LibDiamondCut: Can't add function that already exists"); ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(selector); ds.selectorToFacetAndPosition[selector].facetAddress = _facetAddress; ds.selectorToFacetAndPosition[selector].functionSelectorPosition = selectorPosition; selectorPosition++; } } function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)"); uint16 selectorPosition = uint16(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length); // add new facet address if it does not exist if (selectorPosition == 0) { enforceHasContractCode(_facetAddress, "LibDiamondCut: New facet has no code"); ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = uint16(ds.facetAddresses.length); ds.facetAddresses.push(_facetAddress); } for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; require(oldFacetAddress != _facetAddress, "LibDiamondCut: Can't replace function with same function"); removeFunction(oldFacetAddress, selector); // add function ds.selectorToFacetAndPosition[selector].functionSelectorPosition = selectorPosition; ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(selector); ds.selectorToFacetAndPosition[selector].facetAddress = _facetAddress; selectorPosition++; } } function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); // if function does not exist then do nothing and return require(_facetAddress == address(0), "LibDiamondCut: Remove facet address must be address(0)"); for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; removeFunction(oldFacetAddress, selector); } } function removeFunction(address _facetAddress, bytes4 _selector) internal { DiamondStorage storage ds = diamondStorage(); require(_facetAddress != address(0), "LibDiamondCut: Can't remove function that doesn't exist"); // an immutable function is a function defined directly in a diamond require(_facetAddress != address(this), "LibDiamondCut: Can't remove immutable function"); // replace selector with last selector, then delete last selector uint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition; uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length - 1; // if not the same then replace _selector with lastSelector if (selectorPosition != lastSelectorPosition) { bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition]; ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector; ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint16(selectorPosition); } // delete the last selector ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop(); delete ds.selectorToFacetAndPosition[_selector]; // if no more selectors for facet address then delete the facet address if (lastSelectorPosition == 0) { // replace facet address with last facet address and delete last facet address uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1; uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition; if (facetAddressPosition != lastFacetAddressPosition) { address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition]; ds.facetAddresses[facetAddressPosition] = lastFacetAddress; ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = uint16(facetAddressPosition); } ds.facetAddresses.pop(); delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition; } } function initializeDiamondCut(address _init, bytes memory _calldata) internal { if (_init == address(0)) { require(_calldata.length == 0, "LibDiamondCut: _init is address(0) but_calldata is not empty"); } else { require(_calldata.length > 0, "LibDiamondCut: _calldata is empty but _init is not address(0)"); if (_init != address(this)) { enforceHasContractCode(_init, "LibDiamondCut: _init address has no code"); } (bool success, bytes memory error) = _init.delegatecall(_calldata); if (success == false) { if (error.length > 0) { // bubble up the error revert(string(error)); } else { revert("LibDiamondCut: _init function reverted"); } } } } function enforceHasContractCode(address _contract, string memory _errorMessage) internal view { uint256 contractSize; assembly { contractSize := extcodesize(_contract) } require(contractSize != 0, _errorMessage); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; import {LibDiamond} from "../libraries/LibDiamond.sol"; import {IOwnable} from "../interfaces/IOwnable.sol"; contract OwnershipFacet is IOwnable { function transferOwnership(address _newOwner) external override { LibDiamond.enforceIsContractOwner(); LibDiamond.setContractOwner(_newOwner); } function owner() external view override returns (address owner_) { owner_ = LibDiamond.contractOwner(); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; /******************************************************************************\\ * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen) * EIP-2535 Diamond Standard: https://eips.ethereum.org/EIPS/eip-2535 /******************************************************************************/ import {LibDiamond} from "../libraries/LibDiamond.sol"; import {IDiamondLoupe} from "../interfaces/IDiamondLoupe.sol"; import {IERC165} from "../interfaces/IERC165.sol"; contract DiamondLoupeFacet is IDiamondLoupe, IERC165 { // Diamond Loupe Functions //////////////////////////////////////////////////////////////////// /// These functions are expected to be called frequently by tools. // // struct Facet { // address facetAddress; // bytes4[] functionSelectors; // } /// @notice Gets all facets and their selectors. /// @return facets_ Facet function facets() external view override returns (Facet[] memory facets_) { LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); uint256 numFacets = ds.facetAddresses.length; facets_ = new Facet[](numFacets); for (uint256 i; i < numFacets; i++) { address facetAddress_ = ds.facetAddresses[i]; facets_[i].facetAddress = facetAddress_; facets_[i].functionSelectors = ds.facetFunctionSelectors[facetAddress_].functionSelectors; } } /// @notice Gets all the function selectors provided by a facet. /// @param _facet The facet address. /// @return facetFunctionSelectors_ function facetFunctionSelectors(address _facet) external view override returns (bytes4[] memory facetFunctionSelectors_) { LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); facetFunctionSelectors_ = ds.facetFunctionSelectors[_facet].functionSelectors; } /// @notice Get all the facet addresses used by a diamond. /// @return facetAddresses_ function facetAddresses() external view override returns (address[] memory facetAddresses_) { LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); facetAddresses_ = ds.facetAddresses; } /// @notice Gets the facet that supports the given selector. /// @dev If facet is not found return address(0). /// @param _functionSelector The function selector. /// @return facetAddress_ The facet address. function facetAddress(bytes4 _functionSelector) external view override returns (address facetAddress_) { LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); facetAddress_ = ds.selectorToFacetAndPosition[_functionSelector].facetAddress; } // This implements ERC-165. function supportsInterface(bytes4 _interfaceId) external view override returns (bool) { LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); return ds.supportedInterfaces[_interfaceId]; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; /******************************************************************************\\ * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen) /******************************************************************************/ interface IDiamondCut { enum FacetCutAction {Add, Replace, Remove} struct FacetCut { address facetAddress; FacetCutAction action; bytes4[] functionSelectors; } /// @notice Add/replace/remove any number of functions and optionally execute /// a function with delegatecall /// @param _diamondCut Contains the facet addresses and function selectors /// @param _init The address of the contract or facet to execute _calldata /// @param _calldata A function call, including function selector and arguments /// _calldata is executed with delegatecall on _init function diamondCut( FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata ) external; event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata); }// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; // A loupe is a small magnifying glass used to look at diamonds. // These functions look at diamonds interface IDiamondLoupe { /// These functions are expected to be called frequently /// by tools. struct Facet { address facetAddress; bytes4[] functionSelectors; } /// @notice Gets all facet addresses and their four byte function selectors. /// @return facets_ Facet function facets() external view returns (Facet[] memory facets_); /// @notice Gets all the function selectors supported by a specific facet. /// @param _facet The facet address. /// @return facetFunctionSelectors_ function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetFunctionSelectors_); /// @notice Get all the facet addresses used by a diamond. /// @return facetAddresses_ function facetAddresses() external view returns (address[] memory facetAddresses_); /// @notice Gets the facet that supports the given selector. /// @dev If facet is not found return address(0). /// @param _functionSelector The function selector. /// @return facetAddress_ The facet address. function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_); } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.3.2 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; library LibMeta { bytes32 internal constant EIP712_DOMAIN_TYPEHASH = keccak256(bytes("EIP712Domain(string name,string version,uint256 salt,address verifyingContract)")); function domainSeparator(string memory name, string memory version) internal view returns (bytes32 domainSeparator_) { domainSeparator_ = keccak256( abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256(bytes(name)), keccak256(bytes(version)), getChainID(), address(this)) ); } function getChainID() internal view returns (uint256 id) { assembly { id := chainid() } } function msgSender() internal view returns (address sender_) { if (msg.sender == address(this)) { bytes memory array = msg.data; uint256 index = msg.data.length; assembly { // Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those. sender_ := and(mload(add(array, index)), 0xffffffffffffffffffffffffffffffffffffffff) } } else { sender_ = msg.sender; } } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; /// @title ERC-173 Contract Ownership Standard /// Note: the ERC-165 identifier for this interface is 0x7f5828d0 /* is ERC165 */ interface IOwnable { /// @notice Get the address of the owner /// @return owner_ The address of the owner. function owner() external view returns (address owner_); /// @notice Set the address of the new owner of the contract /// @dev Set _newOwner to address(0) to renounce any ownership. /// @param _newOwner The address of the new owner of the contract function transferOwnership(address _newOwner) external; }
File 2 of 2: NftCommonFacet
// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; pragma experimental ABIEncoderV2; import {NftCommon, Modifiers} from "../libraries/LibAppStorage.sol"; import {LibNftCommon} from "../libraries/LibNftCommon.sol"; import {LibMeta} from "../../shared/libraries/LibMeta.sol"; import {LibERC721} from "../../shared/libraries/LibERC721.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; contract NftCommonFacet is Modifiers { /// @notice Query the universal totalSupply of all NFTs ever minted /// @return totalSupply_ the number of all NFTs that have been minted function totalSupply() external view returns (uint256 totalSupply_) { totalSupply_ = s.tokenIdsCount; } /// @notice Count all NFTs assigned to an owner /// @dev NFTs assigned to the zero address are considered invalid, and this. /// function throws for queries about the zero address. /// @param _owner An address for whom to query the balance /// @return balance_ The number of NFTs owned by `_owner`, possibly zero function balanceOf(address _owner) external view returns (uint256 balance_) { require(_owner != address(0), "NftCommonFacet: _owner can't be address(0)"); balance_ = s.ownerTokenIds[_owner].length; } /// @notice Enumerate valid NFTs /// @dev Throws if `_index` >= `totalSupply()`. /// @param _index A counter less than `totalSupply()` /// @return tokenId_ The token identifier for the `_index`th NFT, /// (sort order not specified) function tokenByIndex(uint256 _index) external view returns (uint256 tokenId_) { require(s.nfts[_index].owner != address(0), "NftCommonFacet: Rebel owner can't be address(0)"); tokenId_ = _index; } /// @notice Enumerate NFTs assigned to an owner /// @dev Throws if `_index` >= `balanceOf(_owner)` or if /// `_owner` is the zero address, representing invalid NFTs. /// @param _owner An address where we are interested in NFTs owned by them /// @param _index A counter less than `balanceOf(_owner)` /// @return tokenId_ The token identifier for the `_index`th NFT assigned to `_owner`, /// (sort order not specified) function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 tokenId_) { require(_index < s.ownerTokenIds[_owner].length, "NftCommonFacet: index beyond owner balance"); tokenId_ = s.ownerTokenIds[_owner][_index]; } /// @notice Get all the Ids of NFTs owned by an address /// @param _owner The address to check for the NFTs /// @return tokenIds_ an array of unsigned integers,each representing the tokenId of each NFT function tokenIdsOfOwner(address _owner) external view returns (uint256[] memory tokenIds_) { tokenIds_ = s.ownerTokenIds[_owner]; } /// @notice Get all details about all the NFTs owned by an address /// @param _owner The address to check for the NFTs /// @return nftCommons_ an array of structs, where each struct contains all the details of each NFT function allNftsOfOwner(address _owner) external view returns (NftCommon[] memory nftCommons_) { uint256 length = s.ownerTokenIds[_owner].length; nftCommons_ = new NftCommon[](length); for (uint256 i; i < length; i++) { nftCommons_[i] = LibNftCommon.getNftCommon(s.ownerTokenIds[_owner][i]); } } /// @notice Get all details about NFT by id /// @param _tokenId The identifier for an NFT /// @return nftCommon_ an struct, which contains all the details about NFT function getNftCommon(uint256 _tokenId) external view returns (NftCommon memory nftCommon_) { return LibNftCommon.getNftCommon(_tokenId); } /// @notice Find the owner of an NFT /// @dev NFTs assigned to zero address are considered invalid, and queries /// about them do throw. /// @param _tokenId The identifier for an NFT /// @return owner_ The address of the owner of the NFT function ownerOf(uint256 _tokenId) external view returns (address owner_) { owner_ = s.nfts[_tokenId].owner; require(owner_ != address(0), "NftCommonFacet: invalid _tokenId"); } /// @notice Get the approved address for a single NFT /// @dev Throws if `_tokenId` is not a valid NFT. /// @param _tokenId The NFT to find the approved address for /// @return approved_ The approved address for this NFT, or the zero address if there is none function getApproved(uint256 _tokenId) external view returns (address approved_) { require(s.nfts[_tokenId].owner != address(0), "ERC721: tokenId is invalid"); approved_ = s.approved[_tokenId]; } /// @notice Query if an address is an authorized operator for another address /// @param _owner The address that owns the NFTs /// @param _operator The address that acts on behalf of the owner /// @return approved_ True if `_operator` is an approved operator for `_owner`, false otherwise function isApprovedForAll(address _owner, address _operator) external view returns (bool approved_) { approved_ = s.operators[_owner][_operator]; } /// @notice Transfers the ownership of an NFT from one address to another address /// @dev Throws unless `LibMeta.msgSender()` is the current owner, an authorized /// operator, or the approved address for this NFT. Throws if `_from` is /// not the current owner. Throws if `_to` is the zero address. Throws if /// `_tokenId` is not a valid NFT. When transfer is complete, this function /// checks if `_to` is a smart contract (code size > 0). If so, it calls /// `onERC721Received` on `_to` and throws if the return value is not /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. /// @param _from The current owner of the NFT /// @param _to The new owner /// @param _tokenId The NFT to transfer /// @param _data Additional data with no specified format, sent in call to `_to` function safeTransferFrom( address _from, address _to, uint256 _tokenId, bytes calldata _data ) external { address sender = LibMeta.msgSender(); internalTransferFrom(sender, _from, _to, _tokenId); LibERC721.checkOnERC721Received(sender, _from, _to, _tokenId, _data); } /// @notice Transfers the ownership of an NFT from one address to another address /// @dev This works identically to the other function with an extra data parameter, /// except this function just sets data to "". /// @param _from The current owner of the NFT /// @param _to The new owner /// @param _tokenId The NFT to transfer function safeTransferFrom( address _from, address _to, uint256 _tokenId ) external { address sender = LibMeta.msgSender(); internalTransferFrom(sender, _from, _to, _tokenId); LibERC721.checkOnERC721Received(sender, _from, _to, _tokenId, ""); } /// @notice Transfers the ownership of multiple NFTs from one address to another at once /// @dev Throws unless `LibMeta.msgSender()` is the current owner, an authorized /// operator, or the approved address of each of the NFTs in `_tokenIds`. Throws if `_from` is /// not the current owner. Throws if `_to` is the zero address. Throws if one of the NFTs in /// `_tokenIds` is not a valid NFT. When transfer is complete, this function /// checks if `_to` is a smart contract (code size > 0). If so, it calls /// `onERC721Received` on `_to` and throws if the return value is not /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. /// @param _from The current owner of the NFTs /// @param _to The new owner /// @param _tokenIds An array containing the identifiers of the NFTs to transfer /// @param _data Additional data with no specified format, sent in call to `_to` function safeBatchTransferFrom( address _from, address _to, uint256[] calldata _tokenIds, bytes calldata _data ) external { address sender = LibMeta.msgSender(); for (uint256 index = 0; index < _tokenIds.length; index++) { uint256 _tokenId = _tokenIds[index]; internalTransferFrom(sender, _from, _to, _tokenId); LibERC721.checkOnERC721Received(sender, _from, _to, _tokenId, _data); } } /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE /// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE /// THEY MAY BE PERMANENTLY LOST /// @dev Throws unless `LibMeta.msgSender()` is the current owner, an authorized /// operator, or the approved address for this NFT. Throws if `_from` is /// not the current owner. Throws if `_to` is the zero address. Throws if /// `_tokenId` is not a valid NFT. /// @param _from The current owner of the NFT /// @param _to The new owner /// @param _tokenId The NFT to transfer function transferFrom( address _from, address _to, uint256 _tokenId ) external { internalTransferFrom(LibMeta.msgSender(), _from, _to, _tokenId); } // This function is used by transfer functions function internalTransferFrom( address _sender, address _from, address _to, uint256 _tokenId ) internal { require(_to != address(0), "NftCommonFacet: Can't transfer to 0 address"); require(_from != address(0), "NftCommonFacet: _from can't be 0 address"); require(_from == s.nfts[_tokenId].owner, "NftCommonFacet: _from is not owner, transfer failed"); require( _sender == _from || s.operators[_from][_sender] || _sender == s.approved[_tokenId], "NftCommonFacet: Not owner or approved to transfer" ); LibNftCommon.transfer(_from, _to, _tokenId); } /// @notice Change or reaffirm the approved address for an NFT /// @dev The zero address indicates there is no approved address. /// Throws unless `LibMeta.msgSender()` is the current NFT owner, or an authorized /// operator of the current owner. /// @param _approved The new approved NFT controller /// @param _tokenId The NFT to approve function approve(address _approved, uint256 _tokenId) external { address owner = s.nfts[_tokenId].owner; require(owner == LibMeta.msgSender() || s.operators[owner][LibMeta.msgSender()], "ERC721: Not owner or operator of token."); s.approved[_tokenId] = _approved; emit LibERC721.Approval(owner, _approved, _tokenId); } /// @notice Enable or disable approval for a third party ("operator") to manage /// all of `LibMeta.msgSender()`'s assets /// @dev Emits the ApprovalForAll event. The contract MUST allow /// multiple operators per owner. /// @param _operator Address to add to the set of authorized operators /// @param _approved True if the operator is approved, false to revoke approval function setApprovalForAll(address _operator, bool _approved) external { s.operators[LibMeta.msgSender()][_operator] = _approved; emit LibERC721.ApprovalForAll(LibMeta.msgSender(), _operator, _approved); } ///@notice Return the universal name of the NFT function name() external view returns (string memory) { return s.name; } /// @notice An abbreviated name for NFTs in this contract function symbol() external view returns (string memory) { return s.symbol; } /// @notice A distinct Uniform Resource Identifier (URI) for a given asset. /// @dev URIs are defined in RFC 3986. The URI may point to a JSON file /// that conforms to the "ERC721 Metadata JSON Schema". function tokenURI(uint256 _tokenId) external view returns (string memory) { return LibNftCommon.tokenBaseURI(_tokenId); } /// @notice Change base URI of the NFT assets metadata /// @param _uri Base URI of the NFT assets metadata function setBaseURI(string memory _uri) external onlyOwner { s.baseURI = _uri; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; import {LibAppStorage, AppStorage, NftCommon} from "./LibAppStorage.sol"; import {LibERC721} from "../../shared/libraries/LibERC721.sol"; import {IERC20} from "../../shared/interfaces/IERC20.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; library LibNftCommon { function getNftCommon(uint256 _tokenId) internal view returns(NftCommon memory) { AppStorage storage s = LibAppStorage.diamondStorage(); return s.nfts[_tokenId]; } function transfer( address _from, address _to, uint256 _tokenId ) internal { AppStorage storage s = LibAppStorage.diamondStorage(); //remove uint256 index = s.ownerTokenIdIndexes[_from][_tokenId]; uint256 lastIndex = s.ownerTokenIds[_from].length - 1; if (index != lastIndex) { uint256 lastTokenId = s.ownerTokenIds[_from][lastIndex]; s.ownerTokenIds[_from][index] = lastTokenId; s.ownerTokenIdIndexes[_from][lastTokenId] = index; } s.ownerTokenIds[_from].pop(); delete s.ownerTokenIdIndexes[_from][_tokenId]; if (s.approved[_tokenId] != address(0)) { delete s.approved[_tokenId]; emit LibERC721.Approval(_from, address(0), _tokenId); } // add setOwner(_tokenId, _to); } function validateAndLowerName(string memory _name) internal pure returns(string memory) { bytes memory name = abi.encodePacked(_name); uint256 len = name.length; require(len != 0, "LibNftCommon: Name can't be 0 chars"); require(len < 26, "LibNftCommon: Name can't be greater than 25 characters"); uint256 char = uint256(uint8(name[0])); require(char != 32, "LibNftCommon: First char of name can't be a space"); char = uint256(uint8(name[len-1])); require(char != 32, "LibNftCommon: Last char of name can't be a space"); for (uint256 i; i < len; i++) { char = uint256(uint8(name[i])); require(char > 31 && char < 127, "LibNftCommon: Invalid character in nft name"); if (char < 91 && char > 64) { name[i] = bytes1(uint8(char+32)); } } return string(name); } function tokenBaseURI(uint256 _tokenId) internal view returns (string memory) { AppStorage storage s = LibAppStorage.diamondStorage(); return bytes(s.baseURI).length > 0 ? string(abi.encodePacked(s.baseURI, Strings.toString(_tokenId))) : s.cloneBoxURI; } function setOwner(uint256 id, address newOwner) internal { AppStorage storage s = LibAppStorage.diamondStorage(); address oldOwner = s.nfts[id].owner; s.nfts[id].owner = newOwner; s.ownerTokenIdIndexes[newOwner][id] = s.ownerTokenIds[newOwner].length; s.ownerTokenIds[newOwner].push(id); emit LibERC721.Transfer(oldOwner, newOwner, id); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; import {LibDiamond} from "../../shared/libraries/LibDiamond.sol"; import {LibMeta} from "../../shared/libraries/LibMeta.sol"; struct NftCommon { uint256 tokenId; string name; address owner; } struct AppStorage { uint256 maxNftCount; uint256 maxNftSalePerUser; uint256 nftSalePrice; string name; string symbol; bytes32 whitelistMerkleRoot; uint256 whitelistSalePrice; mapping(address => bool) whitelistClaimed; bool whitelistIsActive; bool saleIsActive; bool freeMintEnabled; mapping(uint256 => NftCommon) nfts; mapping(address => uint256[]) ownerTokenIds; mapping(address => mapping(uint256 => uint256)) ownerTokenIdIndexes; mapping(uint256 => address) approved; mapping(address => mapping(address => bool)) operators; mapping(string => bool) nftNamesUsed; mapping(address => bool) gameManagers; uint256 tokenIdsCount; string baseURI; string cloneBoxURI; } library LibAppStorage { function diamondStorage() internal pure returns(AppStorage storage ds) { assembly { ds.slot := 0 } } } contract Modifiers { AppStorage internal s; modifier onlyNftOwner(uint256 _tokenId) { require(LibMeta.msgSender() == s.nfts[_tokenId].owner, "LibAppStorage: Only nft owner can call this function"); _; } modifier onlyOwner() { LibDiamond.enforceIsContractOwner(); _; } modifier onlyGameManager() { require(s.gameManagers[LibMeta.msgSender()], "LibAppStorage: Only game manager can call this function"); _; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; import "../interfaces/IERC721TokenReceiver.sol"; library LibERC721 { /// @dev This emits when ownership of any NFT changes by any mechanism. /// This event emits when NFTs are created (`from` == 0) and destroyed /// (`to` == 0). Exception: during contract creation, any number of NFTs /// may be created and assigned without emitting Transfer. At the time of /// any transfer, the approved address for that NFT (if any) is reset to none. event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId); /// @dev This emits when the approved address for an NFT is changed or /// reaffirmed. The zero address indicates there is no approved address. /// When a Transfer event emits, this also indicates that the approved /// address for that NFT (if any) is reset to none. event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId); /// @dev This emits when an operator is enabled or disabled for an owner. /// The operator can manage all NFTs of the owner. event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); bytes4 internal constant ERC721_RECEIVED = 0x150b7a02; function checkOnERC721Received( address _operator, address _from, address _to, uint256 _tokenId, bytes memory _data ) internal { uint256 size; assembly { size := extcodesize(_to) } if (size > 0) { require( ERC721_RECEIVED == IERC721TokenReceiver(_to).onERC721Received(_operator, _from, _tokenId, _data), "DemRebelFacet: Transfer rejected/failed by _to" ); } } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; library LibMeta { bytes32 internal constant EIP712_DOMAIN_TYPEHASH = keccak256(bytes("EIP712Domain(string name,string version,uint256 salt,address verifyingContract)")); function domainSeparator(string memory name, string memory version) internal view returns (bytes32 domainSeparator_) { domainSeparator_ = keccak256( abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256(bytes(name)), keccak256(bytes(version)), getChainID(), address(this)) ); } function getChainID() internal view returns (uint256 id) { assembly { id := chainid() } } function msgSender() internal view returns (address sender_) { if (msg.sender == address(this)) { bytes memory array = msg.data; uint256 index = msg.data.length; assembly { // Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those. sender_ := and(mload(add(array, index)), 0xffffffffffffffffffffffffffffffffffffffff) } } else { sender_ = msg.sender; } } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) pragma solidity ^0.8.0; /** * @dev String operations. */ library Strings { bytes16 private constant _HEX_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) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol 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); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @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] = _HEX_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.7; interface IERC20 { function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address _owner) external view returns (uint256 balance); function transferFrom( address _from, address _to, uint256 _value ) external returns (bool success); function transfer(address _to, uint256 _value) external returns (bool success); function approve(address _spender, uint256 _value) external returns (bool success); function allowance(address _owner, address _spender) external view returns (uint256 remaining); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; /******************************************************************************\\ * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen) * EIP-2535 Diamond Standard: https://eips.ethereum.org/EIPS/eip-2535 /******************************************************************************/ import {IDiamondCut} from "../interfaces/IDiamondCut.sol"; import {IDiamondLoupe} from "../interfaces/IDiamondLoupe.sol"; import {IERC165} from "../interfaces/IERC165.sol"; import {IOwnable} from "../interfaces/IOwnable.sol"; import {LibMeta} from "./LibMeta.sol"; library LibDiamond { bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage"); struct FacetAddressAndPosition { address facetAddress; uint16 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array } struct FacetFunctionSelectors { bytes4[] functionSelectors; uint16 facetAddressPosition; // position of facetAddress in facetAddresses array } struct DiamondStorage { // maps function selector to the facet address and // the position of the selector in the facetFunctionSelectors.selectors array mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition; // maps facet addresses to function selectors mapping(address => FacetFunctionSelectors) facetFunctionSelectors; // facet addresses address[] facetAddresses; // Used to query if a contract implements an interface. // Used to implement ERC-165. mapping(bytes4 => bool) supportedInterfaces; // owner of the contract address contractOwner; } function diamondStorage() internal pure returns (DiamondStorage storage ds) { bytes32 position = DIAMOND_STORAGE_POSITION; assembly { ds.slot := position } } event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); function setContractOwner(address _newOwner) internal { DiamondStorage storage ds = diamondStorage(); address previousOwner = ds.contractOwner; ds.contractOwner = _newOwner; emit OwnershipTransferred(previousOwner, _newOwner); } function contractOwner() internal view returns (address contractOwner_) { contractOwner_ = diamondStorage().contractOwner; } function enforceIsContractOwner() internal view { require(LibMeta.msgSender() == diamondStorage().contractOwner, "LibDiamond: Must be contract owner"); } event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata); function addDiamondFunctions( address _diamondCutFacet, address _diamondLoupeFacet, address _ownershipFacet ) internal { IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](3); bytes4[] memory functionSelectors = new bytes4[](1); functionSelectors[0] = IDiamondCut.diamondCut.selector; cut[0] = IDiamondCut.FacetCut({facetAddress: _diamondCutFacet, action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors}); functionSelectors = new bytes4[](5); functionSelectors[0] = IDiamondLoupe.facets.selector; functionSelectors[1] = IDiamondLoupe.facetFunctionSelectors.selector; functionSelectors[2] = IDiamondLoupe.facetAddresses.selector; functionSelectors[3] = IDiamondLoupe.facetAddress.selector; functionSelectors[4] = IERC165.supportsInterface.selector; cut[1] = IDiamondCut.FacetCut({ facetAddress: _diamondLoupeFacet, action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors }); functionSelectors = new bytes4[](2); functionSelectors[0] = IOwnable.transferOwnership.selector; functionSelectors[1] = IOwnable.owner.selector; cut[2] = IDiamondCut.FacetCut({facetAddress: _ownershipFacet, action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors}); diamondCut(cut, address(0), ""); } // Internal function version of diamondCut function diamondCut( IDiamondCut.FacetCut[] memory _diamondCut, address _init, bytes memory _calldata ) internal { for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) { IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action; if (action == IDiamondCut.FacetCutAction.Add) { addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else if (action == IDiamondCut.FacetCutAction.Replace) { replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else if (action == IDiamondCut.FacetCutAction.Remove) { removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else { revert("LibDiamondCut: Incorrect FacetCutAction"); } } emit DiamondCut(_diamondCut, _init, _calldata); initializeDiamondCut(_init, _calldata); } function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); // uint16 selectorCount = uint16(diamondStorage().selectors.length); require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)"); uint16 selectorPosition = uint16(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length); // add new facet address if it does not exist if (selectorPosition == 0) { enforceHasContractCode(_facetAddress, "LibDiamondCut: New facet has no code"); ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = uint16(ds.facetAddresses.length); ds.facetAddresses.push(_facetAddress); } for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; require(oldFacetAddress == address(0), "LibDiamondCut: Can't add function that already exists"); ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(selector); ds.selectorToFacetAndPosition[selector].facetAddress = _facetAddress; ds.selectorToFacetAndPosition[selector].functionSelectorPosition = selectorPosition; selectorPosition++; } } function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)"); uint16 selectorPosition = uint16(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length); // add new facet address if it does not exist if (selectorPosition == 0) { enforceHasContractCode(_facetAddress, "LibDiamondCut: New facet has no code"); ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = uint16(ds.facetAddresses.length); ds.facetAddresses.push(_facetAddress); } for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; require(oldFacetAddress != _facetAddress, "LibDiamondCut: Can't replace function with same function"); removeFunction(oldFacetAddress, selector); // add function ds.selectorToFacetAndPosition[selector].functionSelectorPosition = selectorPosition; ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(selector); ds.selectorToFacetAndPosition[selector].facetAddress = _facetAddress; selectorPosition++; } } function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); // if function does not exist then do nothing and return require(_facetAddress == address(0), "LibDiamondCut: Remove facet address must be address(0)"); for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; removeFunction(oldFacetAddress, selector); } } function removeFunction(address _facetAddress, bytes4 _selector) internal { DiamondStorage storage ds = diamondStorage(); require(_facetAddress != address(0), "LibDiamondCut: Can't remove function that doesn't exist"); // an immutable function is a function defined directly in a diamond require(_facetAddress != address(this), "LibDiamondCut: Can't remove immutable function"); // replace selector with last selector, then delete last selector uint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition; uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length - 1; // if not the same then replace _selector with lastSelector if (selectorPosition != lastSelectorPosition) { bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition]; ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector; ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint16(selectorPosition); } // delete the last selector ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop(); delete ds.selectorToFacetAndPosition[_selector]; // if no more selectors for facet address then delete the facet address if (lastSelectorPosition == 0) { // replace facet address with last facet address and delete last facet address uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1; uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition; if (facetAddressPosition != lastFacetAddressPosition) { address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition]; ds.facetAddresses[facetAddressPosition] = lastFacetAddress; ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = uint16(facetAddressPosition); } ds.facetAddresses.pop(); delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition; } } function initializeDiamondCut(address _init, bytes memory _calldata) internal { if (_init == address(0)) { require(_calldata.length == 0, "LibDiamondCut: _init is address(0) but_calldata is not empty"); } else { require(_calldata.length > 0, "LibDiamondCut: _calldata is empty but _init is not address(0)"); if (_init != address(this)) { enforceHasContractCode(_init, "LibDiamondCut: _init address has no code"); } (bool success, bytes memory error) = _init.delegatecall(_calldata); if (success == false) { if (error.length > 0) { // bubble up the error revert(string(error)); } else { revert("LibDiamondCut: _init function reverted"); } } } } function enforceHasContractCode(address _contract, string memory _errorMessage) internal view { uint256 contractSize; assembly { contractSize := extcodesize(_contract) } require(contractSize != 0, _errorMessage); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; /******************************************************************************\\ * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen) /******************************************************************************/ interface IDiamondCut { enum FacetCutAction {Add, Replace, Remove} struct FacetCut { address facetAddress; FacetCutAction action; bytes4[] functionSelectors; } /// @notice Add/replace/remove any number of functions and optionally execute /// a function with delegatecall /// @param _diamondCut Contains the facet addresses and function selectors /// @param _init The address of the contract or facet to execute _calldata /// @param _calldata A function call, including function selector and arguments /// _calldata is executed with delegatecall on _init function diamondCut( FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata ) external; event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata); }// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.3.2 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; // A loupe is a small magnifying glass used to look at diamonds. // These functions look at diamonds interface IDiamondLoupe { /// These functions are expected to be called frequently /// by tools. struct Facet { address facetAddress; bytes4[] functionSelectors; } /// @notice Gets all facet addresses and their four byte function selectors. /// @return facets_ Facet function facets() external view returns (Facet[] memory facets_); /// @notice Gets all the function selectors supported by a specific facet. /// @param _facet The facet address. /// @return facetFunctionSelectors_ function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetFunctionSelectors_); /// @notice Get all the facet addresses used by a diamond. /// @return facetAddresses_ function facetAddresses() external view returns (address[] memory facetAddresses_); /// @notice Gets the facet that supports the given selector. /// @dev If facet is not found return address(0). /// @param _functionSelector The function selector. /// @return facetAddress_ The facet address. function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; /// @title ERC-173 Contract Ownership Standard /// Note: the ERC-165 identifier for this interface is 0x7f5828d0 /* is ERC165 */ interface IOwnable { /// @notice Get the address of the owner /// @return owner_ The address of the owner. function owner() external view returns (address owner_); /// @notice Set the address of the new owner of the contract /// @dev Set _newOwner to address(0) to renounce any ownership. /// @param _newOwner The address of the new owner of the contract function transferOwnership(address _newOwner) external; } // SPDX-License-Identifier: MIT pragma solidity ^0.8.7; /// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02. interface IERC721TokenReceiver { /// @notice Handle the receipt of an NFT /// @dev The ERC721 smart contract calls this function on the recipient /// after a `transfer`. This function MAY throw to revert and reject the /// transfer. Return of other than the magic value MUST result in the /// transaction being reverted. /// Note: the contract address is always the message sender. /// @param _operator The address which called `safeTransferFrom` function /// @param _from The address which previously owned the token /// @param _tokenId The NFT identifier which is being transferred /// @param _data Additional data with no specified format /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` /// unless throwing function onERC721Received( address _operator, address _from, uint256 _tokenId, bytes calldata _data ) external returns (bytes4); }