Contract Source Code:
//SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "@limitbreak/tm-core-lib/src/token/erc721/ERC721C.sol";
contract Test721C is ERC721C {
address public owner;
uint256 nextTokenId;
constructor()
ERC721C("Test", "T")
CreatorTokenBase(0x3203c3f64312AF9344e42EF8Aa45B97C9DFE4594)
{
owner = msg.sender;
}
function _requireCallerIsContractOwner() internal view override {
if (msg.sender != owner) revert();
}
function mint(uint256 count) external {
uint256 tokenId = nextTokenId;
uint256 endTokenId = nextTokenId = tokenId + count;
for (; tokenId < endTokenId; ++tokenId) {
_mint(msg.sender, tokenId);
}
}
}
pragma solidity ^0.8.4;
import "./ERC721.sol";
import "../../utils/token/AutomaticValidatorTransferApproval.sol";
import "../../utils/token/CreatorTokenBase.sol";
import "../../utils/token/Constants.sol";
abstract contract ERC721CBase is ERC721, CreatorTokenBase, AutomaticValidatorTransferApproval {
constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) { }
/**
* @notice Overrides behavior of isApprovedFor all such that if an operator is not explicitly approved
* for all, the contract owner can optionally auto-approve the 721-C transfer validator for transfers.
*/
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool isApproved) {
isApproved = super.isApprovedForAll(owner, operator);
if (!isApproved) {
if (storageAutomaticValidatorTransferApproval().autoApproveTransfersFromValidator) {
isApproved = operator == address(getTransferValidator());
}
}
}
/**
* @notice Indicates whether the contract implements the specified interface.
* @dev Overrides supportsInterface in ERC165.
* @param interfaceId The interface id
* @return true if the contract implements the specified interface, false otherwise
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return
interfaceId == type(ICreatorToken).interfaceId ||
interfaceId == type(ICreatorTokenLegacy).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @notice Returns the function selector for the transfer validator's validation function to be called
* @notice for transaction simulation.
*/
function getTransferValidationFunction() external pure returns (bytes4 functionSignature, bool isViewFunction) {
functionSignature = bytes4(keccak256("validateTransfer(address,address,address,uint256)"));
isViewFunction = true;
}
function _validateTransfer(
address caller,
address from,
address to,
uint256 tokenId,
uint256 value
) internal virtual override {
_preValidateTransfer(caller, from, to, tokenId, value);
}
function _tokenType() internal pure override returns(uint16) {
return uint16(TOKEN_TYPE_ERC721);
}
}
/**
* @title ERC721C
* @author Limit Break, Inc.
* @notice Extends OpenZeppelin's ERC721 implementation with Creator Token functionality, which
* allows the contract owner to update the transfer validation logic by managing a security policy in
* an external transfer validation security policy registry. See {CreatorTokenTransferValidator}.
*/
abstract contract ERC721C is ERC721CBase {
constructor(string memory name_, string memory symbol_) ERC721CBase(name_, symbol_) { }
}
abstract contract ERC721CInitializable is ERC721CBase {
struct StorageERC721Initializable {
bool erc721Initialized;
}
bytes32 private constant ERC721_INITIALIZABLE_STORAGE_SLOT = keccak256("storage.ERC721Initializable");
function storageERC721Initializable() internal pure returns (StorageERC721Initializable storage ptr) {
bytes32 slot = ERC721_INITIALIZABLE_STORAGE_SLOT;
assembly {
ptr.slot := slot
}
}
error ERC721OpenZeppelinInitializable__AlreadyInitializedERC721();
constructor() ERC721CBase("", "") { }
/// @dev Initializes parameters of ERC721 tokens.
/// These cannot be set in the constructor because this contract is optionally compatible with EIP-1167.
function initializeERC721(string memory name_, string memory symbol_) public virtual {
_requireCallerIsContractOwner();
if(storageERC721Initializable().erc721Initialized) {
revert ERC721OpenZeppelinInitializable__AlreadyInitializedERC721();
}
storageERC721Initializable().erc721Initialized = true;
StorageERC721.data().name = name_;
StorageERC721.data().symbol = symbol_;
_emitDefaultTransferValidator();
_registerTokenType(getTransferValidator());
}
}
pragma solidity ^0.8.4;
import "./IERC721.sol";
import "./IERC721Errors.sol";
import "./IERC721Receiver.sol";
import "./IERC721Metadata.sol";
import "./StorageERC721.sol";
import "../../utils/introspection/ERC165.sol";
import "../../utils/misc/Strings.sol";
import "../../utils/token/TransferHooks.sol";
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension, but not including the Enumerable extension, which is available separately as
* {ERC721Enumerable}.
*/
abstract contract ERC721 is TransferHooks, ERC165, IERC721, IERC721Metadata, IERC721Errors {
using Strings for uint256;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
constructor(string memory name_, string memory symbol_) {
StorageERC721.data().name = name_;
StorageERC721.data().symbol = symbol_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-balanceOf}.
*/
function balanceOf(address owner) public view virtual returns (uint256) {
if (owner == address(0)) {
revert ERC721InvalidOwner(address(0));
}
return StorageERC721.data().balances[owner];
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual returns (address owner_) {
owner_ = StorageERC721.data().owners[tokenId];
if (owner_ == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
}
/**
* @dev See {IERC721Metadata-name}.
*/
function name() public view virtual returns (string memory) {
return StorageERC721.data().name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/
function symbol() public view virtual returns (string memory) {
return StorageERC721.data().symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(uint256 tokenId) public view virtual returns (string memory) {
address owner_ = StorageERC721.data().owners[tokenId];
if (owner_ == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return "";
}
/**
* @dev See {IERC721-approve}.
*/
function approve(address to, uint256 tokenId) public virtual {
address owner_ = StorageERC721.data().owners[tokenId];
if (owner_ == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
if (owner_ != msg.sender) {
if (!isApprovedForAll(owner_, msg.sender)) {
revert ERC721InvalidApprover(msg.sender);
}
}
emit Approval(owner_, to, tokenId);
StorageERC721.data().tokenApprovals[tokenId] = to;
}
/**
* @dev See {IERC721-getApproved}.
*/
function getApproved(uint256 tokenId) public view virtual returns (address) {
if (StorageERC721.data().owners[tokenId] == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
return StorageERC721.data().tokenApprovals[tokenId];
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual {
if (operator == address(0)) {
revert ERC721InvalidOperator(operator);
}
StorageERC721.data().operatorApprovals[_getOperatorApprovalKey(msg.sender, operator)] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {
return StorageERC721.data().operatorApprovals[_getOperatorApprovalKey(owner, operator)];
}
/**
* @dev See {IERC721-transferFrom}.
*/
function transferFrom(address from, address to, uint256 tokenId) public virtual {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = StorageERC721.data().owners[tokenId];
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
_validateTransfer(msg.sender, from, to, tokenId, 0);
if (from == msg.sender ||
isApprovedForAll(from, msg.sender) ||
StorageERC721.data().tokenApprovals[tokenId] == msg.sender) {
// Clear approval. No need to re-authorize or emit the Approval event
StorageERC721.data().tokenApprovals[tokenId] = address(0);
unchecked {
--StorageERC721.data().balances[from];
++StorageERC721.data().balances[to];
}
StorageERC721.data().owners[tokenId] = to;
emit Transfer(from, to, tokenId);
} else {
revert ERC721InsufficientApproval(msg.sender, tokenId);
}
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
transferFrom(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
/**
* @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance.
*
* Requirements:
*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeMint(address to, uint256 tokenId) internal {
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(to, tokenId);
_checkOnERC721Received(address(0), to, tokenId, data);
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/
function _mint(address to, uint256 tokenId) internal {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
if (StorageERC721.data().owners[tokenId] != address(0)) {
revert ERC721InvalidSender(address(0));
}
_validateMint(msg.sender, to, tokenId, msg.value);
unchecked {
++StorageERC721.data().balances[to];
}
StorageERC721.data().owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
* This is an internal function that does not check if the sender is authorized to operate on the token.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId) internal {
address from = StorageERC721.data().owners[tokenId];
if (from == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
_validateBurn(msg.sender, from, tokenId, msg.value);
// Clear approval. No need to re-authorize or emit the Approval event
StorageERC721.data().tokenApprovals[tokenId] = address(0);
unchecked {
--StorageERC721.data().balances[from];
}
StorageERC721.data().owners[tokenId] = address(0);
emit Transfer(from, address(0), tokenId);
}
/**
* @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the
* recipient doesn't accept the token transfer. The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param data bytes optional data to send along with the call
*/
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private {
if (to.code.length > 0) {
try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data) returns (bytes4 retval) {
if (retval != IERC721Receiver.onERC721Received.selector) {
revert ERC721InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
revert ERC721InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
function _getOperatorApprovalKey(address owner_, address operator_) internal pure returns (bytes32 key) {
assembly {
mstore(0x00, owner_)
mstore(0x20, operator_)
key := keccak256(0x00, 0x40)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "../access/OwnablePermissions.sol";
/**
* @title AutomaticValidatorTransferApproval
* @author Limit Break, Inc.
* @notice Base contract mix-in that provides boilerplate code giving the contract owner the
* option to automatically approve a 721-C transfer validator implementation for transfers.
*/
abstract contract AutomaticValidatorTransferApproval is OwnablePermissions {
struct StorageAutomaticValidatorTransferApproval {
bool autoApproveTransfersFromValidator;
}
bytes32 private constant AUTOMATIC_VALIDATOR_TRANSFER_APPROVAL_STORAGE_SLOT = keccak256("storage.AutomaticValidatorTransferApproval");
function storageAutomaticValidatorTransferApproval() internal pure returns (StorageAutomaticValidatorTransferApproval storage ptr) {
bytes32 slot = AUTOMATIC_VALIDATOR_TRANSFER_APPROVAL_STORAGE_SLOT;
assembly {
ptr.slot := slot
}
}
/// @dev Emitted when the automatic approval flag is modified by the creator.
event AutomaticApprovalOfTransferValidatorSet(bool autoApproved);
/**
* @notice Sets if the transfer validator is automatically approved as an operator for all token owners.
*
* @dev Throws when the caller is not the contract owner.
*
* @param autoApprove If true, the collection's transfer validator will be automatically approved to
* transfer holder's tokens.
*/
function setAutomaticApprovalOfTransfersFromValidator(bool autoApprove) external {
_requireCallerIsContractOwner();
storageAutomaticValidatorTransferApproval().autoApproveTransfersFromValidator = autoApprove;
emit AutomaticApprovalOfTransferValidatorSet(autoApprove);
}
function autoApproveTransfersFromValidator() public view returns (bool) {
return storageAutomaticValidatorTransferApproval().autoApproveTransfersFromValidator;
}
}
pragma solidity ^0.8.4;
import "./ICreatorToken.sol";
import "./ICreatorTokenLegacy.sol";
import "./ITransferValidator.sol";
import "./ITransferValidatorSetTokenType.sol";
import "./TransferValidation.sol";
import "../access/OwnablePermissions.sol";
/**
* @title CreatorTokenBase
* @author Limit Break, Inc.
* @notice CreatorTokenBaseV3 is an abstract contract that provides basic functionality for managing token
* transfer policies through an implementation of ICreatorTokenTransferValidator/ICreatorTokenTransferValidatorV2/ICreatorTokenTransferValidatorV3.
* This contract is intended to be used as a base for creator-specific token contracts, enabling customizable transfer
* restrictions and security policies.
*
* <h4>Features:</h4>
* <ul>Ownable: This contract can have an owner who can set and update the transfer validator.</ul>
* <ul>TransferValidation: Implements the basic token transfer validation interface.</ul>
*
* <h4>Benefits:</h4>
* <ul>Provides a flexible and modular way to implement custom token transfer restrictions and security policies.</ul>
* <ul>Allows creators to enforce policies such as account and codehash blacklists, whitelists, and graylists.</ul>
* <ul>Can be easily integrated into other token contracts as a base contract.</ul>
*
* <h4>Intended Usage:</h4>
* <ul>Use as a base contract for creator token implementations that require advanced transfer restrictions and
* security policies.</ul>
* <ul>Set and update the ICreatorTokenTransferValidator implementation contract to enforce desired policies for the
* creator token.</ul>
*
* <h4>Compatibility:</h4>
* <ul>Backward and Forward Compatible - V1/V2/V3 Creator Token Base will work with V1/V2/V3 Transfer Validators.</ul>
*/
abstract contract CreatorTokenBase is OwnablePermissions, TransferValidation, ICreatorToken {
struct StorageCreatorToken {
bool isValidatorInitialized;
address transferValidator;
}
bytes32 private constant CREATOR_TOKEN_STORAGE_SLOT = keccak256("storage.CreatorToken");
function storageCreatorToken() internal pure returns (StorageCreatorToken storage ptr) {
bytes32 slot = CREATOR_TOKEN_STORAGE_SLOT;
assembly {
ptr.slot := slot
}
}
/// @dev Thrown when setting a transfer validator address that has no deployed code.
error CreatorTokenBase__InvalidTransferValidatorContract();
/// @dev The default transfer validator that will be used if no transfer validator has been set by the creator.
address private immutable DEFAULT_TRANSFER_VALIDATOR;
constructor(address defaultTransferValidator_) {
DEFAULT_TRANSFER_VALIDATOR = defaultTransferValidator_;
_emitDefaultTransferValidator();
_registerTokenType(DEFAULT_TRANSFER_VALIDATOR);
}
/**
* @notice Sets the transfer validator for the token contract.
*
* @dev Throws when provided validator contract is not the zero address and does not have code.
* @dev Throws when the caller is not the contract owner.
*
* @dev <h4>Postconditions:</h4>
* 1. The transferValidator address is updated.
* 2. The `TransferValidatorUpdated` event is emitted.
*
* @param transferValidator_ The address of the transfer validator contract.
*/
function setTransferValidator(address transferValidator_) public {
_requireCallerIsContractOwner();
bool isValidTransferValidator = transferValidator_.code.length > 0;
if(transferValidator_ != address(0) && !isValidTransferValidator) {
revert CreatorTokenBase__InvalidTransferValidatorContract();
}
emit TransferValidatorUpdated(address(getTransferValidator()), transferValidator_);
storageCreatorToken().isValidatorInitialized = true;
storageCreatorToken().transferValidator = transferValidator_;
_registerTokenType(transferValidator_);
}
/**
* @notice Returns the default transfer validator contract address for this token contract.
* This validator will be used if no transfer validator has been set by the creator.
*/
function getDefaultTransferValidator() public virtual view returns (address) {
return DEFAULT_TRANSFER_VALIDATOR;
}
/**
* @notice Returns the transfer validator contract address for this token contract.
*/
function getTransferValidator() public view override returns (address validator) {
validator = storageCreatorToken().transferValidator;
if (validator == address(0)) {
if (!storageCreatorToken().isValidatorInitialized) {
validator = getDefaultTransferValidator();
}
}
}
/**
* @dev Pre-validates a token transfer, reverting if the transfer is not allowed by this token's security policy.
* Inheriting contracts are responsible for overriding the _beforeTokenTransfer function, or its equivalent
* and calling _validateBeforeTransfer so that checks can be properly applied during token transfers.
*
* @dev Be aware that if the msg.sender is the transfer validator, the transfer is automatically permitted, as the
* transfer validator is expected to pre-validate the transfer.
*
* @dev Throws when the transfer doesn't comply with the collection's transfer policy, if the transferValidator is
* set to a non-zero address.
*
* @param caller The address of the caller.
* @param from The address of the sender.
* @param to The address of the receiver.
* @param tokenId The token id being transferred.
*/
function _preValidateTransfer(
address caller,
address from,
address to,
uint256 tokenId,
uint256 /*value*/) internal virtual override {
address validator = getTransferValidator();
if (validator != address(0)) {
if (msg.sender == validator) {
return;
}
ITransferValidator(validator).validateTransfer(caller, from, to, tokenId);
}
}
/**
* @dev Pre-validates a token transfer, reverting if the transfer is not allowed by this token's security policy.
* Inheriting contracts are responsible for overriding the _beforeTokenTransfer function, or its equivalent
* and calling _validateBeforeTransfer so that checks can be properly applied during token transfers.
*
* @dev Be aware that if the msg.sender is the transfer validator, the transfer is automatically permitted, as the
* transfer validator is expected to pre-validate the transfer.
*
* @dev Used for ERC20 and ERC1155 token transfers which have an amount value to validate in the transfer validator.
* @dev The `tokenId` for ERC20 tokens should be set to `0`.
*
* @dev Throws when the transfer doesn't comply with the collection's transfer policy, if the transferValidator is
* set to a non-zero address.
*
* @param caller The address of the caller.
* @param from The address of the sender.
* @param to The address of the receiver.
* @param tokenId The token id being transferred.
* @param amount The amount of token being transferred.
*/
function _preValidateTransfer(
address caller,
address from,
address to,
uint256 tokenId,
uint256 amount,
uint256 /*value*/) internal virtual override {
address validator = getTransferValidator();
if (validator != address(0)) {
if (msg.sender == validator) {
return;
}
ITransferValidator(validator).validateTransfer(caller, from, to, tokenId, amount);
}
}
function _tokenType() internal virtual pure returns(uint16);
function _registerTokenType(address validator) internal {
if (validator != address(0)) {
uint256 validatorCodeSize;
assembly {
validatorCodeSize := extcodesize(validator)
}
if(validatorCodeSize > 0) {
try ITransferValidatorSetTokenType(validator).setTokenTypeOfCollection(address(this), _tokenType()) {
} catch { }
}
}
}
/**
* @dev Used during contract deployment for constructable and cloneable creator tokens
* @dev to emit the `TransferValidatorUpdated` event signaling the validator for the contract
* @dev is the default transfer validator.
*/
function _emitDefaultTransferValidator() internal {
emit TransferValidatorUpdated(address(0), getDefaultTransferValidator());
}
}
pragma solidity ^0.8.4;
/// @dev Constant value representing the ERC721 token type for signatures and transfer hooks
uint256 constant TOKEN_TYPE_ERC721 = 721;
/// @dev Constant value representing the ERC1155 token type for signatures and transfer hooks
uint256 constant TOKEN_TYPE_ERC1155 = 1155;
/// @dev Constant value representing the ERC20 token type for signatures and transfer hooks
uint256 constant TOKEN_TYPE_ERC20 = 20;
pragma solidity ^0.8.4;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
pragma solidity ^0.8.4;
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
pragma solidity ^0.8.4;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
pragma solidity ^0.8.4;
import "./IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Metadata is IERC721 {
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}
pragma solidity ^0.8.4;
library StorageERC721 {
bytes32 private constant DATA_STORAGE_SLOT = keccak256("storage.ERC721");
struct Data {
string name;
string symbol;
mapping(uint256 => address) owners;
mapping(address => uint256) balances;
mapping(uint256 => address) tokenApprovals;
mapping(bytes32 => bool) operatorApprovals;
}
function data() internal pure returns (Data storage ptr) {
bytes32 slot = DATA_STORAGE_SLOT;
assembly {
ptr.slot := slot
}
}
}
pragma solidity ^0.8.4;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
pragma solidity ^0.8.4;
import "../math/Math.sol";
import "../math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
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_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
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);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}
pragma solidity ^0.8.4;
abstract contract TransferHooks {
/*************************************************************************/
/* Transfers Without Amounts */
/*************************************************************************/
/// @dev Optional validation hook that fires before a mint
function _validateMint(address caller, address to, uint256 tokenId, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires before a burn
function _validateBurn(address caller, address from, uint256 tokenId, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires before a transfer
function _validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 value) internal virtual {}
/*************************************************************************/
/* Transfers With Amounts */
/*************************************************************************/
/// @dev Optional validation hook that fires before a mint
function _validateMint(address caller, address to, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires before a burn
function _validateBurn(address caller, address from, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires before a transfer
function _validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}
}
pragma solidity ^0.8.4;
abstract contract OwnablePermissions {
function _requireCallerIsContractOwner() internal view virtual;
}
pragma solidity ^0.8.4;
interface ICreatorToken {
event TransferValidatorUpdated(address oldValidator, address newValidator);
function getTransferValidator() external view returns (address validator);
function setTransferValidator(address validator) external;
function getTransferValidationFunction() external view returns (bytes4 functionSignature, bool isViewFunction);
}
pragma solidity ^0.8.4;
interface ICreatorTokenLegacy {
event TransferValidatorUpdated(address oldValidator, address newValidator);
function getTransferValidator() external view returns (address validator);
function setTransferValidator(address validator) external;
}
pragma solidity ^0.8.4;
interface ITransferValidator {
function applyCollectionTransferPolicy(address caller, address from, address to) external view;
function validateTransfer(address caller, address from, address to) external view;
function validateTransfer(address caller, address from, address to, uint256 tokenId) external view;
function validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount) external;
function beforeAuthorizedTransfer(address operator, address token, uint256 tokenId) external;
function afterAuthorizedTransfer(address token, uint256 tokenId) external;
function beforeAuthorizedTransfer(address operator, address token) external;
function afterAuthorizedTransfer(address token) external;
function beforeAuthorizedTransfer(address token, uint256 tokenId) external;
function beforeAuthorizedTransferWithAmount(address token, uint256 tokenId, uint256 amount) external;
function afterAuthorizedTransferWithAmount(address token, uint256 tokenId) external;
}
pragma solidity ^0.8.4;
interface ITransferValidatorSetTokenType {
function setTokenTypeOfCollection(address collection, uint16 tokenType) external;
}
pragma solidity ^0.8.4;
/**
* @title TransferValidation
* @author Limit Break, Inc.
* @notice A mix-in that can be combined with ERC-721 contracts to provide more granular hooks.
* Openzeppelin's ERC721 contract only provides hooks for before and after transfer. This allows
* developers to validate or customize transfers within the context of a mint, a burn, or a transfer.
*/
abstract contract TransferValidation {
/// @dev Thrown when the from and to address are both the zero address.
error ShouldNotMintToBurnAddress();
/*************************************************************************/
/* Transfers Without Amounts */
/*************************************************************************/
/// @dev Inheriting contracts should call this function in the _beforeTokenTransfer function to get more granular hooks.
function _validateBeforeTransfer(address from, address to, uint256 tokenId) internal virtual {
bool fromZeroAddress = from == address(0);
bool toZeroAddress = to == address(0);
if(fromZeroAddress && toZeroAddress) {
revert ShouldNotMintToBurnAddress();
} else if(fromZeroAddress) {
_preValidateMint(msg.sender, to, tokenId, msg.value);
} else if(toZeroAddress) {
_preValidateBurn(msg.sender, from, tokenId, msg.value);
} else {
_preValidateTransfer(msg.sender, from, to, tokenId, msg.value);
}
}
/// @dev Inheriting contracts should call this function in the _afterTokenTransfer function to get more granular hooks.
function _validateAfterTransfer(address from, address to, uint256 tokenId) internal virtual {
bool fromZeroAddress = from == address(0);
bool toZeroAddress = to == address(0);
if(fromZeroAddress && toZeroAddress) {
revert ShouldNotMintToBurnAddress();
} else if(fromZeroAddress) {
_postValidateMint(msg.sender, to, tokenId, msg.value);
} else if(toZeroAddress) {
_postValidateBurn(msg.sender, from, tokenId, msg.value);
} else {
_postValidateTransfer(msg.sender, from, to, tokenId, msg.value);
}
}
/// @dev Optional validation hook that fires before a mint
function _preValidateMint(address caller, address to, uint256 tokenId, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires after a mint
function _postValidateMint(address caller, address to, uint256 tokenId, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires before a burn
function _preValidateBurn(address caller, address from, uint256 tokenId, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires after a burn
function _postValidateBurn(address caller, address from, uint256 tokenId, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires before a transfer
function _preValidateTransfer(address caller, address from, address to, uint256 tokenId, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires after a transfer
function _postValidateTransfer(address caller, address from, address to, uint256 tokenId, uint256 value) internal virtual {}
/*************************************************************************/
/* Transfers With Amounts */
/*************************************************************************/
/// @dev Inheriting contracts should call this function in the _beforeTokenTransfer function to get more granular hooks.
function _validateBeforeTransfer(address from, address to, uint256 tokenId, uint256 amount) internal virtual {
bool fromZeroAddress = from == address(0);
bool toZeroAddress = to == address(0);
if(fromZeroAddress && toZeroAddress) {
revert ShouldNotMintToBurnAddress();
} else if(fromZeroAddress) {
_preValidateMint(msg.sender, to, tokenId, amount, msg.value);
} else if(toZeroAddress) {
_preValidateBurn(msg.sender, from, tokenId, amount, msg.value);
} else {
_preValidateTransfer(msg.sender, from, to, tokenId, amount, msg.value);
}
}
/// @dev Inheriting contracts should call this function in the _afterTokenTransfer function to get more granular hooks.
function _validateAfterTransfer(address from, address to, uint256 tokenId, uint256 amount) internal virtual {
bool fromZeroAddress = from == address(0);
bool toZeroAddress = to == address(0);
if(fromZeroAddress && toZeroAddress) {
revert ShouldNotMintToBurnAddress();
} else if(fromZeroAddress) {
_postValidateMint(msg.sender, to, tokenId, amount, msg.value);
} else if(toZeroAddress) {
_postValidateBurn(msg.sender, from, tokenId, amount, msg.value);
} else {
_postValidateTransfer(msg.sender, from, to, tokenId, amount, msg.value);
}
}
/// @dev Optional validation hook that fires before a mint
function _preValidateMint(address caller, address to, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires after a mint
function _postValidateMint(address caller, address to, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires before a burn
function _preValidateBurn(address caller, address from, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires after a burn
function _postValidateBurn(address caller, address from, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires before a transfer
function _preValidateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires after a transfer
function _postValidateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount, uint256 value) internal virtual {}
}
pragma solidity ^0.8.4;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* 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[ERC 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);
}
pragma solidity ^0.8.4;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
pragma solidity ^0.8.4;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}