文件 1 的 13:Clones.sol
pragma solidity 0.8.9;
library Clones {
function clone(address implementation) internal returns (address instance) {
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, implementation))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
instance := create(0, ptr, 0x37)
}
require(instance != address(0), "ERC1167: create failed");
}
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, implementation))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
instance := create2(0, ptr, 0x37, salt)
}
require(instance != address(0), "ERC1167: create2 failed");
}
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, implementation))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
mstore(add(ptr, 0x38), shl(0x60, deployer))
mstore(add(ptr, 0x4c), salt)
mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
predicted := keccak256(add(ptr, 0x37), 0x55)
}
}
function predictDeterministicAddress(address implementation, bytes32 salt)
internal
view
returns (address predicted)
{
return predictDeterministicAddress(implementation, salt, address(this));
}
}
文件 2 的 13:Context.sol
pragma solidity 0.8.9;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 3 的 13:ERC165.sol
pragma solidity 0.8.9;
import "../interfaces/IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 4 的 13:GenericErrors.sol
pragma solidity 0.8.9;
abstract contract GenericErrors {
string internal constant ERROR_INPUT_ARRAY_EMPTY = "Input array empty";
string internal constant ERROR_INPUT_ARRAY_SIZE_MISMATCH = "Input array size mismatch";
string internal constant ERROR_INVALID_MSG_SENDER = "Invalid msg.sender";
string internal constant ERROR_UNEXPECTED_DATA_SIGNER = "Unexpected data signer";
string internal constant ERROR_INSUFFICIENT_BALANCE = "Insufficient balance";
string internal constant ERROR_WITHDRAW_UNSUCCESSFUL = "Withdraw unsuccessful";
string internal constant ERROR_CONTRACT_IS_FINALIZED = "Contract is finalized";
string internal constant ERROR_CANNOT_CHANGE_DEFAULT_OWNER = "Cannot change default owner";
string internal constant ERROR_UNCLONEABLE_REFERENCE_CONTRACT = "Uncloneable reference contract";
string internal constant ERROR_BIPS_OVER_100_PERCENT = "Bips over 100%";
string internal constant ERROR_NO_ROYALTY_RECEIVER = "No royalty receiver";
string internal constant ERROR_REINITIALIZATION_NOT_PERMITTED = "Re-initialization not permitted";
string internal constant ERROR_ZERO_ETH_TRANSFER = "Zero ETH Transfer";
}
文件 5 的 13:IERC165.sol
pragma solidity 0.8.9;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 6 的 13:IERC721.sol
pragma solidity 0.8.9;
import "./IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
}
文件 7 的 13:IERC721Cloneable.sol
pragma solidity 0.8.9;
import "./IERC721.sol";
interface IERC721Cloneable is IERC721 {
function initializeERC721(string calldata name_, string calldata symbol_, string calldata baseURI_) external;
}
文件 8 的 13:IERC721DefaultOwnerCloneable.sol
pragma solidity 0.8.9;
import "./IERC165.sol";
interface IERC721DefaultOwnerCloneable is IERC165 {
function initializeDefaultOwner(address defaultOwner_) external;
}
文件 9 的 13:IERC721MetadataGenerator.sol
pragma solidity 0.8.9;
import "./IERC165.sol";
interface IERC721MetadataGenerator is IERC165 {
function tokenMetadata(uint256 tokenId, uint256 niftyType, bytes calldata data) external view returns (string memory);
}
文件 10 的 13:INiftyEntityCloneable.sol
pragma solidity 0.8.9;
import "./IERC165.sol";
interface INiftyEntityCloneable is IERC165 {
function initializeNiftyEntity(address niftyRegistryContract_) external;
}
文件 11 的 13:INiftyRegistry.sol
pragma solidity 0.8.9;
interface INiftyRegistry {
function isValidNiftySender(address sendingKey) external view returns (bool);
}
文件 12 的 13:NiftyCloneFactory.sol
pragma solidity 0.8.9;
import "../interfaces/IERC721Cloneable.sol";
import "../interfaces/IERC721DefaultOwnerCloneable.sol";
import "../interfaces/IERC721MetadataGenerator.sol";
import "../interfaces/INiftyEntityCloneable.sol";
import "../libraries/Clones.sol";
import "../utils/NiftyPermissions.sol";
contract NiftyCloneFactory is NiftyPermissions {
event ClonedERC721(address newToken);
event ClonedERC721MetadataGenerator(address metadataGenerator);
constructor(address niftyRegistryContract_) {
initializeNiftyEntity(niftyRegistryContract_);
}
function cloneERC721(address implementation, address niftyRegistryContract_, address defaultOwner_, string calldata name_, string calldata symbol_, string calldata baseURI_) external returns (address) {
_requireOnlyValidSender();
require(IERC165(implementation).supportsInterface(type(IERC721Cloneable).interfaceId), "Not a valid ERC721 Token");
address clone = Clones.clone(implementation);
emit ClonedERC721(clone);
IERC721Cloneable(clone).initializeERC721(name_, symbol_, baseURI_);
if(IERC165(implementation).supportsInterface(type(INiftyEntityCloneable).interfaceId)) {
INiftyEntityCloneable(clone).initializeNiftyEntity(niftyRegistryContract_);
}
if(IERC165(implementation).supportsInterface(type(IERC721DefaultOwnerCloneable).interfaceId)) {
IERC721DefaultOwnerCloneable(clone).initializeDefaultOwner(defaultOwner_);
}
return clone;
}
function cloneMetadataGenerator(address implementation, address niftyRegistryContract_) external returns (address) {
_requireOnlyValidSender();
require(IERC165(implementation).supportsInterface(type(IERC721MetadataGenerator).interfaceId), "Not a valid Metadata Generator");
address clone = Clones.clone(implementation);
emit ClonedERC721MetadataGenerator(clone);
if(IERC165(implementation).supportsInterface(type(INiftyEntityCloneable).interfaceId)) {
INiftyEntityCloneable(clone).initializeNiftyEntity(niftyRegistryContract_);
}
return clone;
}
}
文件 13 的 13:NiftyPermissions.sol
pragma solidity 0.8.9;
import "./ERC165.sol";
import "./GenericErrors.sol";
import "../interfaces/INiftyEntityCloneable.sol";
import "../interfaces/INiftyRegistry.sol";
import "../libraries/Context.sol";
abstract contract NiftyPermissions is Context, ERC165, GenericErrors, INiftyEntityCloneable {
event AdminTransferred(address indexed previousAdmin, address indexed newAdmin);
bool internal initializedNiftyEntity;
address public admin;
address public nominatedAdmin;
INiftyRegistry internal permissionsRegistry;
function initializeNiftyEntity(address niftyRegistryContract_) public {
require(!initializedNiftyEntity, ERROR_REINITIALIZATION_NOT_PERMITTED);
permissionsRegistry = INiftyRegistry(niftyRegistryContract_);
initializedNiftyEntity = true;
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(INiftyEntityCloneable).interfaceId ||
super.supportsInterface(interfaceId);
}
function renounceAdmin() external {
_requireOnlyValidSender();
_transferAdmin(address(0));
}
function nominateAdmin(address nominee) external {
_requireOnlyValidSender();
nominatedAdmin = nominee;
}
function acceptAdmin() external {
address nominee = nominatedAdmin;
require(_msgSender() == nominee, ERROR_INVALID_MSG_SENDER);
_transferAdmin(nominee);
}
function _requireOnlyValidSender() internal view {
address currentAdmin = admin;
if(currentAdmin == address(0)) {
require(permissionsRegistry.isValidNiftySender(_msgSender()), ERROR_INVALID_MSG_SENDER);
} else {
require(_msgSender() == currentAdmin, ERROR_INVALID_MSG_SENDER);
}
}
function _transferAdmin(address newAdmin) internal {
address oldAdmin = admin;
admin = newAdmin;
delete nominatedAdmin;
emit AdminTransferred(oldAdmin, newAdmin);
}
}
{
"compilationTarget": {
"contracts/infrastructure/NiftyCloneFactory.sol": "NiftyCloneFactory"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1500
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"niftyRegistryContract_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newToken","type":"address"}],"name":"ClonedERC721","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"metadataGenerator","type":"address"}],"name":"ClonedERC721MetadataGenerator","type":"event"},{"inputs":[],"name":"acceptAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"},{"internalType":"address","name":"niftyRegistryContract_","type":"address"},{"internalType":"address","name":"defaultOwner_","type":"address"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"string","name":"baseURI_","type":"string"}],"name":"cloneERC721","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"},{"internalType":"address","name":"niftyRegistryContract_","type":"address"}],"name":"cloneMetadataGenerator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"niftyRegistryContract_","type":"address"}],"name":"initializeNiftyEntity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nominee","type":"address"}],"name":"nominateAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]