Contract Name:
AuthorizedTransferSecurityRegistryCreator
Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {
ImmutableCreate2FactoryInterface
} from "seaport-types/src/interfaces/ImmutableCreate2FactoryInterface.sol";
import {
IAuthorizedTransferSecurityRegistry
} from "./interfaces/IAuthorizedTransferSecurityRegistry.sol";
/// @title AuthorizedTransferSecurityRegistryCreator
/// @dev Deploys and configures AuthorizedTransferSecurityRegistry contracts.
contract AuthorizedTransferSecurityRegistryCreator {
ImmutableCreate2FactoryInterface private constant factory = (
ImmutableCreate2FactoryInterface(
0x0000000000FFe8B47B3e2130213B802212439497
)
);
error UNAUTHORIZED();
function _canDeploy() internal view returns (bool) {
return
tx.origin == address(0x939C8d89EBC11fA45e576215E2353673AD0bA18A) ||
tx.origin == address(0xe80a65eB7a3018DedA407e621Ef5fb5B416678CA) ||
tx.origin == address(0x86D26897267711ea4b173C8C124a0A73612001da) ||
tx.origin == address(0xbF81D02F3Ee59E79af3D9337a186F65c9faE39F3);
}
function deployAndConfigure(
address initialOwner,
bytes32 salt,
bytes memory initializationCode,
address[] memory authorizers,
address[] memory operators
) external returns (address registry) {
if (!_canDeploy()) {
revert UNAUTHORIZED();
}
IAuthorizedTransferSecurityRegistry registryContract = (
IAuthorizedTransferSecurityRegistry(
factory.safeCreate2(salt, initializationCode)
)
);
// Note: in provided init code, this contract must be supplied
// as the default owner so that it can perform configuration and
// reassign ownership of the default list to the supplied owner.
if (authorizers.length > 0) {
registryContract.addAuthorizers(0, authorizers);
}
if (operators.length > 0) {
registryContract.addOperators(0, operators);
}
registryContract.reassignOwnershipOfList(0, initialOwner);
return address(registryContract);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
/**
* @title ImmutableCreate2FactoryInterface
* @author 0age
* @notice This contract provides a safeCreate2 function that takes a salt value
* and a block of initialization code as arguments and passes them into
* inline assembly. The contract prevents redeploys by maintaining a
* mapping of all contracts that have already been deployed, and
* prevents frontrunning or other collisions by requiring that the first
* 20 bytes of the salt are equal to the address of the caller (this can
* be bypassed by setting the first 20 bytes to the null address). There
* is also a view function that computes the address of the contract
* that will be created when submitting a given salt or nonce along with
* a given block of initialization code.
*/
interface ImmutableCreate2FactoryInterface {
/**
* @dev Create a contract using CREATE2 by submitting a given salt or nonce
* along with the initialization code for the contract. Note that the
* first 20 bytes of the salt must match those of the calling address,
* which prevents contract creation events from being submitted by
* unintended parties.
*
* @param salt The nonce that will be passed into the CREATE2
* call.
* @param initializationCode The initialization code that will be passed
* into the CREATE2 call.
*
* @return deploymentAddress Address of the contract that will be created.
*/
function safeCreate2(
bytes32 salt,
bytes calldata initializationCode
) external payable returns (address deploymentAddress);
/**
* @dev Compute the address of the contract that will be created when
* submitting a given salt or nonce to the contract along with the
* contract's initialization code. The CREATE2 address is computed in
* accordance with EIP-1014, and adheres to the formula therein of
* `keccak256( 0xff ++ address ++ salt ++ keccak256(init_code)))[12:]`
* when performing the computation. The computed address is then
* checked for any existing contract code - if so, the null address
* will be returned instead.
*
* @param salt The nonce passed into the CREATE2 address calculation.
* @param initCode The contract initialization code to be used that will be
* passed into the CREATE2 address calculation.
*
* @return deploymentAddress Address of the contract that will be created,
* or the null address if a contract already
* exists at that address.
*/
function findCreate2Address(
bytes32 salt,
bytes calldata initCode
) external view returns (address deploymentAddress);
/**
* @dev Compute the address of the contract that will be created when
* submitting a given salt or nonce to the contract along with the
* keccak256 hash of the contract's initialization code. The CREATE2
* address is computed in accordance with EIP-1014, and adheres to the
* `keccak256( 0xff ++ address ++ salt ++ keccak256(init_code)))[12:]`
* formula when performing the computation. The computed address is
* then checked for any existing contract code - if so, the null
* address will be returned instead.
*
* @param salt The nonce passed into the CREATE2 address
* calculation.
* @param initCodeHash The keccak256 hash of the initialization code that
* will be passed into the CREATE2 address calculation.
*
* @return deploymentAddress Address of the contract that will be created,
* or the null address if a contract already
* exists at that address.
*/
function findCreate2AddressViaHash(
bytes32 salt,
bytes32 initCodeHash
) external view returns (address deploymentAddress);
/**
* @dev Determine if a contract has already been deployed by the factory to
* a given address.
*
* @param deploymentAddress The contract address to check.
*
* @return True if the contract has been deployed, false otherwise.
*/
function hasBeenDeployed(
address deploymentAddress
) external view returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
enum ListTypes {
AuthorizerList,
OperatorList
}
/// @title IAuthorizedTransferSecurityRegistry
/// @dev Interface for the Authorized Transfer Security Registry, a simplified version of the Transfer
/// Security Registry that only supports authorizers and whitelisted operators, and assumes a
/// security level of OperatorWhitelistEnableOTC + authorizers for all collections that use it.
/// Note that a number of view functions on collections that add this validator will not work.
interface IAuthorizedTransferSecurityRegistry {
event CreatedList(uint256 indexed id, string name);
event AppliedListToCollection(address indexed collection, uint120 indexed id);
event ReassignedListOwnership(uint256 indexed id, address indexed newOwner);
event AddedAccountToList(ListTypes indexed kind, uint256 indexed id, address indexed account);
event RemovedAccountFromList(ListTypes indexed kind, uint256 indexed id, address indexed account);
error AuthorizedTransferSecurityRegistry__ListDoesNotExist();
error AuthorizedTransferSecurityRegistry__CallerDoesNotOwnList();
error AuthorizedTransferSecurityRegistry__ArrayLengthCannotBeZero();
error AuthorizedTransferSecurityRegistry__CallerMustHaveElevatedPermissionsForSpecifiedNFT();
error AuthorizedTransferSecurityRegistry__ListOwnershipCannotBeTransferredToZeroAddress();
error AuthorizedTransferSecurityRegistry__ZeroAddressNotAllowed();
error AuthorizedTransferSecurityRegistry__UnauthorizedTransfer();
error AuthorizedTransferSecurityRegistry__CallerIsNotValidAuthorizer();
/// Manage lists of authorizers & operators that can be applied to collections
function createList(string calldata name) external returns (uint120);
function createListCopy(string calldata name, uint120 sourceListId) external returns (uint120);
function reassignOwnershipOfList(uint120 id, address newOwner) external;
function renounceOwnershipOfList(uint120 id) external;
function applyListToCollection(address collection, uint120 id) external;
function listOwners(uint120 id) external view returns (address);
/// Manage and query for authorizers on lists
function addAuthorizers(uint120 id, address[] calldata accounts) external;
function removeAuthorizers(uint120 id, address[] calldata accounts) external;
function getAuthorizers(uint120 id) external view returns (address[] memory);
function isAuthorizer(uint120 id, address account) external view returns (bool);
function getAuthorizersByCollection(address collection) external view returns (address[] memory);
function isAuthorizerByCollection(address collection, address account) external view returns (bool);
/// Manage and query for operators on lists
function addOperators(uint120 id, address[] calldata accounts) external;
function removeOperators(uint120 id, address[] calldata accounts) external;
function getOperators(uint120 id) external view returns (address[] memory);
function isOperator(uint120 id, address account) external view returns (bool);
function getOperatorsByCollection(address collection) external view returns (address[] memory);
function isOperatorByCollection(address collection, address account) external view returns (bool);
/// Ensure that a specific operator has been authorized to transfer tokens
function validateTransfer(address caller, address from, address to) external view;
/// Ensure that a transfer has been authorized for a specific tokenId
function validateTransfer(address caller, address from, address to, uint256 tokenId) external view;
/// Ensure that a transfer has been authorized for a specific amount of a specific tokenId, and
/// reduce the transferable amount remaining
function validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount) external;
/// Legacy alias for validateTransfer (address caller, address from, address to)
function applyCollectionTransferPolicy(address caller, address from, address to) external view;
/// Temporarily assign a specific allowed operator for a given collection
function beforeAuthorizedTransfer(address operator, address token) external;
/// Clear assignment of a specific allowed operator for a given collection
function afterAuthorizedTransfer(address token) external;
/// Temporarily allow a specific tokenId from a given collection to be transferred
function beforeAuthorizedTransfer(address token, uint256 tokenId) external;
/// Clear assignment of an specific tokenId's transfer allowance
function afterAuthorizedTransfer(address token, uint256 tokenId) external;
/// Temporarily allow a specific amount of a specific tokenId from a given collection to be transferred
function beforeAuthorizedTransferWithAmount(address token, uint256 tokenId, uint256 amount) external;
/// Clear assignment of a tokenId's transfer allowance for a specific amount
function afterAuthorizedTransferWithAmount(address token, uint256 tokenId) external;
}