Overview
APE Balance
APE Value
$0.00More Info
Private Name Tags
ContractCreator
TokenTracker
Multichain Info
Latest 25 from a total of 46,150 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Set Approval For... | 33769932 | 21 mins ago | IN | 0 APE | 0.0046652 | ||||
| Set Approval For... | 33768536 | 1 hr ago | IN | 0 APE | 0.0046652 | ||||
| Set Approval For... | 33767536 | 1 hr ago | IN | 0 APE | 0.0046652 | ||||
| Set Approval For... | 33767403 | 2 hrs ago | IN | 0 APE | 0.0046652 | ||||
| Set Approval For... | 33764883 | 3 hrs ago | IN | 0 APE | 0.0046652 | ||||
| Set Approval For... | 33764615 | 3 hrs ago | IN | 0 APE | 0.0046652 | ||||
| Set Approval For... | 33762095 | 6 hrs ago | IN | 0 APE | 0.00467374 | ||||
| Set Approval For... | 33762055 | 6 hrs ago | IN | 0 APE | 0.0046652 | ||||
| Set Approval For... | 33761617 | 6 hrs ago | IN | 0 APE | 0.00244567 | ||||
| Set Approval For... | 33761105 | 7 hrs ago | IN | 0 APE | 0.00467374 | ||||
| Set Approval For... | 33750995 | 12 hrs ago | IN | 0 APE | 0.00243713 | ||||
| Set Approval For... | 33749175 | 14 hrs ago | IN | 0 APE | 0.0046652 | ||||
| Set Approval For... | 33742941 | 19 hrs ago | IN | 0 APE | 0.0046652 | ||||
| Set Approval For... | 33741292 | 20 hrs ago | IN | 0 APE | 0.0046652 | ||||
| Set Approval For... | 33740797 | 20 hrs ago | IN | 0 APE | 0.00467374 | ||||
| Set Approval For... | 33734304 | 24 hrs ago | IN | 0 APE | 0.0046652 | ||||
| Set Approval For... | 33731938 | 25 hrs ago | IN | 0 APE | 0.0046652 | ||||
| Safe Transfer Fr... | 33728411 | 28 hrs ago | IN | 0 APE | 0.0054267 | ||||
| Set Approval For... | 33726933 | 29 hrs ago | IN | 0 APE | 0.0046652 | ||||
| Set Approval For... | 33726147 | 30 hrs ago | IN | 0 APE | 0.0046652 | ||||
| Set Approval For... | 33720914 | 35 hrs ago | IN | 0 APE | 0.0046652 | ||||
| Set Approval For... | 33720150 | 35 hrs ago | IN | 0 APE | 0.0046652 | ||||
| Set Approval For... | 33712663 | 40 hrs ago | IN | 0 APE | 0.0046652 | ||||
| Set Approval For... | 33711947 | 41 hrs ago | IN | 0 APE | 0.00467374 | ||||
| Set Approval For... | 33711386 | 41 hrs ago | IN | 0 APE | 0.0046652 |
View more zero value Internal Transactions in Advanced View mode
Cross-Chain Transactions
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.28;
import {ERC1155} from "solady/tokens/ERC1155.sol";
import {OwnableRoles} from "solady/auth/OwnableRoles.sol";
import {ERC2981} from "solady/tokens/ERC2981.sol";
import {EIP712} from "solady/utils/EIP712.sol";
import {SignatureCheckerLib} from "solady/utils/SignatureCheckerLib.sol";
import {ICreatorToken} from "src/interfaces/ICreatorToken.sol";
import {ITransferValidator} from "src/interfaces/ITransferValidator.sol";
contract MetaMaskAvatars is ERC1155, ERC2981, OwnableRoles, ICreatorToken, EIP712 {
error TokenDoesNotExist(uint256 tokenId);
error InvalidSignature();
error AlreadyClaimed();
event Claimed(address indexed minter, uint256 indexed tokenId, bytes32 subscriptionIdHash);
uint256 public constant URI_MANAGEMENT_ROLE = _ROLE_0;
uint256 public constant MINTING_ROLE = _ROLE_1;
/// @dev keccak256("Claim(address minter,bytes32 subscriptionIdHash,uint256 tokenId)")
bytes32 public constant CLAIM_TYPEHASH = 0x921c01b2b37f8037e35da681d2e3f2cf2bdbd04ce87fa03672dcb3092ea9b12f;
address public signer;
address internal _transferValidator;
mapping(uint256 tokenId => string tokenURI) public tokenURIs;
mapping(uint256 tokenId => uint256 supply) public totalSupply;
mapping(bytes32 subscriptionIdHash => mapping(uint256 tokenId => bool)) public claimed;
constructor(
address _owner,
address _minter,
address _uriManager,
address _signer,
address _royaltyRecipient,
uint96 _royaltyBips,
address _transferValidatorAddress
) {
_initializeOwner(_owner);
_grantRoles(_minter, MINTING_ROLE);
_grantRoles(_uriManager, URI_MANAGEMENT_ROLE);
signer = _signer;
_setDefaultRoyalty(_royaltyRecipient, _royaltyBips);
if (_transferValidatorAddress != address(0)) {
_transferValidator = _transferValidatorAddress;
emit TransferValidatorUpdated(address(0), _transferValidatorAddress);
}
}
/// @notice Returns the EIP-712 domain name and version.
function _domainNameAndVersion() internal pure override returns (string memory _name, string memory _version) {
_name = "MetaMask Avatars";
_version = "1";
}
/// @notice Claim an avatar with a backend-signed authorization.
/// @param tokenId The token to claim (1 = Common, 2 = Rare)
/// @param subscriptionIdHash keccak256(lowercase(subscriptionId))
/// @param signature EIP-712 signature from backend
function claim(uint256 tokenId, bytes32 subscriptionIdHash, bytes calldata signature) external {
if (!_tokenExists(tokenId)) {
revert TokenDoesNotExist(tokenId);
}
if (claimed[subscriptionIdHash][tokenId]) {
revert AlreadyClaimed();
}
bytes32 structHash = keccak256(abi.encode(CLAIM_TYPEHASH, msg.sender, subscriptionIdHash, tokenId));
bytes32 digest = _hashTypedData(structHash);
if (!SignatureCheckerLib.isValidSignatureNowCalldata(signer, digest, signature)) {
revert InvalidSignature();
}
claimed[subscriptionIdHash][tokenId] = true;
_mint(msg.sender, tokenId, 1, "");
emit Claimed(msg.sender, tokenId, subscriptionIdHash);
}
/// @notice Update the authorized signer.
function setSigner(address _signer) external onlyOwner {
signer = _signer;
}
/// @notice Check if a subscription has claimed a token.
function hasClaimed(bytes32 subscriptionIdHash, uint256 tokenId) external view returns (bool) {
return claimed[subscriptionIdHash][tokenId];
}
/// @notice Exposes the EIP-712 typed data hash for testing.
function hashTypedData(bytes32 structHash) external view returns (bytes32) {
return _hashTypedData(structHash);
}
function setTokenURI(uint256 tokenId, string memory tokenURI) external onlyRolesOrOwner(URI_MANAGEMENT_ROLE) {
tokenURIs[tokenId] = tokenURI;
emit URI(tokenURI, tokenId);
}
function mint(address to, uint256 tokenId) external onlyRolesOrOwner(MINTING_ROLE) {
if (!_tokenExists(tokenId)) {
revert TokenDoesNotExist(tokenId);
}
_mint(to, tokenId, 1, "");
}
function batchMint(address to, uint256[] memory tokenIds, uint256[] memory amounts)
external
onlyRolesOrOwner(MINTING_ROLE)
{
for (uint256 i = 0; i < tokenIds.length; i++) {
if (!_tokenExists(tokenIds[i])) {
revert TokenDoesNotExist(tokenIds[i]);
}
}
_batchMint(to, tokenIds, amounts, "");
}
function batchAirdrop(address[] calldata recipients, uint256[] calldata tokenIds)
external
onlyRolesOrOwner(MINTING_ROLE)
{
if (recipients.length != tokenIds.length) revert ArrayLengthsMismatch();
for (uint256 i = 0; i < recipients.length; i++) {
if (!_tokenExists(tokenIds[i])) revert TokenDoesNotExist(tokenIds[i]);
_mint(recipients[i], tokenIds[i], 1, "");
}
}
function burn(address from, uint256 id, uint256 amount) external {
if (msg.sender != from && !isApprovedForAll(from, msg.sender)) {
revert NotOwnerNorApproved();
}
_burn(from, id, amount);
}
function batchBurn(address from, uint256[] memory ids, uint256[] memory amounts) external {
if (msg.sender != from && !isApprovedForAll(from, msg.sender)) {
revert NotOwnerNorApproved();
}
_batchBurn(from, ids, amounts);
}
function setDefaultRoyalty(address receiver, uint96 feeNumerator) external onlyOwner {
_setDefaultRoyalty(receiver, feeNumerator);
}
function setTransferValidator(address transferValidator) external override onlyOwner {
address oldValidator = _transferValidator;
_transferValidator = transferValidator;
emit TransferValidatorUpdated(oldValidator, transferValidator);
}
function getTransferValidator() external view override returns (address) {
return _transferValidator;
}
function getTransferValidationFunction() external pure override returns (bytes4, bool) {
return (0x1854b241, false);
}
function uri(uint256 tokenId) public view override returns (string memory) {
return tokenURIs[tokenId];
}
function supportsInterface(bytes4 interfaceId) public view override(ERC1155, ERC2981) returns (bool) {
return ERC1155.supportsInterface(interfaceId) || ERC2981.supportsInterface(interfaceId)
|| interfaceId == type(ICreatorToken).interfaceId;
}
function name() external pure returns (string memory) {
return "MetaMask + Otherside";
}
function symbol() external pure returns (string memory) {
return "FoxNaut";
}
function _useBeforeTokenTransfer() internal pure override returns (bool) {
return true;
}
function _beforeTokenTransfer(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory /* data */
) internal override {
if (from != address(0) && to != address(0)) {
address validator = _transferValidator;
if (validator != address(0)) {
for (uint256 i = 0; i < ids.length; i++) {
ITransferValidator(validator).validateTransfer(msg.sender, from, to, ids[i], amounts[i]);
}
}
} else if (from == address(0)) {
for (uint256 i = 0; i < ids.length; i++) {
totalSupply[ids[i]] += amounts[i];
}
} else if (to == address(0)) {
for (uint256 i = 0; i < ids.length; i++) {
totalSupply[ids[i]] -= amounts[i];
}
}
}
function _tokenExists(uint256 tokenId) internal view returns (bool) {
return bytes(tokenURIs[tokenId]).length > 0;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple ERC1155 implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC1155.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC1155/ERC1155.sol)
///
/// @dev Note:
/// - The ERC1155 standard allows for self-approvals.
/// For performance, this implementation WILL NOT revert for such actions.
/// Please add any checks with overrides if desired.
/// - The transfer functions use the identity precompile (0x4)
/// to copy memory internally.
///
/// If you are overriding:
/// - Make sure all variables written to storage are properly cleaned
// (e.g. the bool value for `isApprovedForAll` MUST be either 1 or 0 under the hood).
/// - Check that the overridden function is actually used in the function you want to
/// change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC1155 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The lengths of the input arrays are not the same.
error ArrayLengthsMismatch();
/// @dev Cannot mint or transfer to the zero address.
error TransferToZeroAddress();
/// @dev The recipient's balance has overflowed.
error AccountBalanceOverflow();
/// @dev Insufficient balance.
error InsufficientBalance();
/// @dev Only the token owner or an approved account can manage the tokens.
error NotOwnerNorApproved();
/// @dev Cannot safely transfer to a contract that does not implement
/// the ERC1155Receiver interface.
error TransferToNonERC1155ReceiverImplementer();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Emitted when `amount` of token `id` is transferred
/// from `from` to `to` by `operator`.
event TransferSingle(
address indexed operator,
address indexed from,
address indexed to,
uint256 id,
uint256 amount
);
/// @dev Emitted when `amounts` of token `ids` are transferred
/// from `from` to `to` by `operator`.
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] amounts
);
/// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens.
event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved);
/// @dev Emitted when the Uniform Resource Identifier (URI) for token `id`
/// is updated to `value`. This event is not used in the base contract.
/// You may need to emit this event depending on your URI logic.
///
/// See: https://eips.ethereum.org/EIPS/eip-1155#metadata
event URI(string value, uint256 indexed id);
/// @dev `keccak256(bytes("TransferSingle(address,address,address,uint256,uint256)"))`.
uint256 private constant _TRANSFER_SINGLE_EVENT_SIGNATURE =
0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62;
/// @dev `keccak256(bytes("TransferBatch(address,address,address,uint256[],uint256[])"))`.
uint256 private constant _TRANSFER_BATCH_EVENT_SIGNATURE =
0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb;
/// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`.
uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =
0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The `ownerSlotSeed` of a given owner is given by.
/// ```
/// let ownerSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, owner))
/// ```
///
/// The balance slot of `owner` is given by.
/// ```
/// mstore(0x20, ownerSlotSeed)
/// mstore(0x00, id)
/// let balanceSlot := keccak256(0x00, 0x40)
/// ```
///
/// The operator approval slot of `owner` is given by.
/// ```
/// mstore(0x20, ownerSlotSeed)
/// mstore(0x00, operator)
/// let operatorApprovalSlot := keccak256(0x0c, 0x34)
/// ```
uint256 private constant _ERC1155_MASTER_SLOT_SEED = 0x9a31110384e0b0c9;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1155 METADATA */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the URI for token `id`.
///
/// You can either return the same templated URI for all token IDs,
/// (e.g. "https://example.com/api/{id}.json"),
/// or return a unique URI for each `id`.
///
/// See: https://eips.ethereum.org/EIPS/eip-1155#metadata
function uri(uint256 id) public view virtual returns (string memory);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1155 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the amount of `id` owned by `owner`.
function balanceOf(address owner, uint256 id) public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
mstore(0x14, owner)
mstore(0x00, id)
result := sload(keccak256(0x00, 0x40))
}
}
/// @dev Returns whether `operator` is approved to manage the tokens of `owner`.
function isApprovedForAll(address owner, address operator)
public
view
virtual
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
mstore(0x14, owner)
mstore(0x00, operator)
result := sload(keccak256(0x0c, 0x34))
}
}
/// @dev Sets whether `operator` is approved to manage the tokens of the caller.
///
/// Emits a {ApprovalForAll} event.
function setApprovalForAll(address operator, bool isApproved) public virtual {
/// @solidity memory-safe-assembly
assembly {
// Convert to 0 or 1.
isApproved := iszero(iszero(isApproved))
// Update the `isApproved` for (`msg.sender`, `operator`).
mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
mstore(0x14, caller())
mstore(0x00, operator)
sstore(keccak256(0x0c, 0x34), isApproved)
// Emit the {ApprovalForAll} event.
mstore(0x00, isApproved)
// forgefmt: disable-next-line
log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator)))
}
}
/// @dev Transfers `amount` of `id` from `from` to `to`.
///
/// Requirements:
/// - `to` cannot be the zero address.
/// - `from` must have at least `amount` of `id`.
/// - If the caller is not `from`,
/// it must be approved to manage the tokens of `from`.
/// - If `to` refers to a smart contract, it must implement
/// {ERC1155-onERC1155Received}, which is called upon a batch transfer.
///
/// Emits a {TransferSingle} event.
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes calldata data
) public virtual {
if (_useBeforeTokenTransfer()) {
_beforeTokenTransfer(from, to, _single(id), _single(amount), data);
}
/// @solidity memory-safe-assembly
assembly {
let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, from))
let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, to))
mstore(0x20, fromSlotSeed)
// Clear the upper 96 bits.
from := shr(96, fromSlotSeed)
to := shr(96, toSlotSeed)
// Revert if `to` is the zero address.
if iszero(to) {
mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
revert(0x1c, 0x04)
}
// If the caller is not `from`, do the authorization check.
if iszero(eq(caller(), from)) {
mstore(0x00, caller())
if iszero(sload(keccak256(0x0c, 0x34))) {
mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
revert(0x1c, 0x04)
}
}
// Subtract and store the updated balance of `from`.
{
mstore(0x00, id)
let fromBalanceSlot := keccak256(0x00, 0x40)
let fromBalance := sload(fromBalanceSlot)
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
sstore(fromBalanceSlot, sub(fromBalance, amount))
}
// Increase and store the updated balance of `to`.
{
mstore(0x20, toSlotSeed)
let toBalanceSlot := keccak256(0x00, 0x40)
let toBalanceBefore := sload(toBalanceSlot)
let toBalanceAfter := add(toBalanceBefore, amount)
if lt(toBalanceAfter, toBalanceBefore) {
mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
revert(0x1c, 0x04)
}
sstore(toBalanceSlot, toBalanceAfter)
}
// Emit a {TransferSingle} event.
mstore(0x20, amount)
log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), from, to)
}
if (_useAfterTokenTransfer()) {
_afterTokenTransfer(from, to, _single(id), _single(amount), data);
}
/// @solidity memory-safe-assembly
assembly {
// Do the {onERC1155Received} check if `to` is a smart contract.
if extcodesize(to) {
// Prepare the calldata.
let m := mload(0x40)
// `onERC1155Received(address,address,uint256,uint256,bytes)`.
mstore(m, 0xf23a6e61)
mstore(add(m, 0x20), caller())
mstore(add(m, 0x40), from)
mstore(add(m, 0x60), id)
mstore(add(m, 0x80), amount)
mstore(add(m, 0xa0), 0xa0)
mstore(add(m, 0xc0), data.length)
calldatacopy(add(m, 0xe0), data.offset, data.length)
// Revert if the call reverts.
if iszero(call(gas(), to, 0, add(m, 0x1c), add(0xc4, data.length), m, 0x20)) {
if returndatasize() {
// Bubble up the revert if the call reverts.
returndatacopy(m, 0x00, returndatasize())
revert(m, returndatasize())
}
}
// Load the returndata and compare it with the function selector.
if iszero(eq(mload(m), shl(224, 0xf23a6e61))) {
mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Transfers `amounts` of `ids` from `from` to `to`.
///
/// Requirements:
/// - `to` cannot be the zero address.
/// - `from` must have at least `amount` of `id`.
/// - `ids` and `amounts` must have the same length.
/// - If the caller is not `from`,
/// it must be approved to manage the tokens of `from`.
/// - If `to` refers to a smart contract, it must implement
/// {ERC1155-onERC1155BatchReceived}, which is called upon a batch transfer.
///
/// Emits a {TransferBatch} event.
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) public virtual {
if (_useBeforeTokenTransfer()) {
_beforeTokenTransfer(from, to, ids, amounts, data);
}
/// @solidity memory-safe-assembly
assembly {
if iszero(eq(ids.length, amounts.length)) {
mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
revert(0x1c, 0x04)
}
let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, from))
let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, to))
mstore(0x20, fromSlotSeed)
// Clear the upper 96 bits.
from := shr(96, fromSlotSeed)
to := shr(96, toSlotSeed)
// Revert if `to` is the zero address.
if iszero(to) {
mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
revert(0x1c, 0x04)
}
// If the caller is not `from`, do the authorization check.
if iszero(eq(caller(), from)) {
mstore(0x00, caller())
if iszero(sload(keccak256(0x0c, 0x34))) {
mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
revert(0x1c, 0x04)
}
}
// Loop through all the `ids` and update the balances.
{
for { let i := shl(5, ids.length) } i {} {
i := sub(i, 0x20)
let amount := calldataload(add(amounts.offset, i))
// Subtract and store the updated balance of `from`.
{
mstore(0x20, fromSlotSeed)
mstore(0x00, calldataload(add(ids.offset, i)))
let fromBalanceSlot := keccak256(0x00, 0x40)
let fromBalance := sload(fromBalanceSlot)
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
sstore(fromBalanceSlot, sub(fromBalance, amount))
}
// Increase and store the updated balance of `to`.
{
mstore(0x20, toSlotSeed)
let toBalanceSlot := keccak256(0x00, 0x40)
let toBalanceBefore := sload(toBalanceSlot)
let toBalanceAfter := add(toBalanceBefore, amount)
if lt(toBalanceAfter, toBalanceBefore) {
mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
revert(0x1c, 0x04)
}
sstore(toBalanceSlot, toBalanceAfter)
}
}
}
// Emit a {TransferBatch} event.
{
let m := mload(0x40)
// Copy the `ids`.
mstore(m, 0x40)
let n := shl(5, ids.length)
mstore(add(m, 0x40), ids.length)
calldatacopy(add(m, 0x60), ids.offset, n)
// Copy the `amounts`.
mstore(add(m, 0x20), add(0x60, n))
let o := add(add(m, n), 0x60)
mstore(o, ids.length)
calldatacopy(add(o, 0x20), amounts.offset, n)
// Do the emit.
log4(m, add(add(n, n), 0x80), _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), from, to)
}
}
if (_useAfterTokenTransfer()) {
_afterTokenTransferCalldata(from, to, ids, amounts, data);
}
/// @solidity memory-safe-assembly
assembly {
// Do the {onERC1155BatchReceived} check if `to` is a smart contract.
if extcodesize(to) {
mstore(0x00, to) // Cache `to` to prevent stack too deep.
let m := mload(0x40)
// Prepare the calldata.
// `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`.
mstore(m, 0xbc197c81)
mstore(add(m, 0x20), caller())
mstore(add(m, 0x40), from)
// Copy the `ids`.
mstore(add(m, 0x60), 0xa0)
let n := shl(5, ids.length)
mstore(add(m, 0xc0), ids.length)
calldatacopy(add(m, 0xe0), ids.offset, n)
// Copy the `amounts`.
mstore(add(m, 0x80), add(0xc0, n))
let o := add(add(m, n), 0xe0)
mstore(o, ids.length)
calldatacopy(add(o, 0x20), amounts.offset, n)
// Copy the `data`.
mstore(add(m, 0xa0), add(add(0xe0, n), n))
o := add(add(o, n), 0x20)
mstore(o, data.length)
calldatacopy(add(o, 0x20), data.offset, data.length)
let nAll := add(0x104, add(data.length, add(n, n)))
// Revert if the call reverts.
if iszero(call(gas(), mload(0x00), 0, add(mload(0x40), 0x1c), nAll, m, 0x20)) {
if returndatasize() {
// Bubble up the revert if the call reverts.
returndatacopy(m, 0x00, returndatasize())
revert(m, returndatasize())
}
}
// Load the returndata and compare it with the function selector.
if iszero(eq(mload(m), shl(224, 0xbc197c81))) {
mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Returns the amounts of `ids` for `owners.
///
/// Requirements:
/// - `owners` and `ids` must have the same length.
function balanceOfBatch(address[] calldata owners, uint256[] calldata ids)
public
view
virtual
returns (uint256[] memory balances)
{
/// @solidity memory-safe-assembly
assembly {
if iszero(eq(ids.length, owners.length)) {
mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
revert(0x1c, 0x04)
}
balances := mload(0x40)
mstore(balances, ids.length)
let o := add(balances, 0x20)
let i := shl(5, ids.length)
mstore(0x40, add(i, o))
// Loop through all the `ids` and load the balances.
for {} i {} {
i := sub(i, 0x20)
let owner := calldataload(add(owners.offset, i))
mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, shl(96, owner)))
mstore(0x00, calldataload(add(ids.offset, i)))
mstore(add(o, i), sload(keccak256(0x00, 0x40)))
}
}
}
/// @dev Returns true if this contract implements the interface defined by `interfaceId`.
/// See: https://eips.ethereum.org/EIPS/eip-165
/// This function call must use less than 30000 gas.
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
let s := shr(224, interfaceId)
// ERC165: 0x01ffc9a7, ERC1155: 0xd9b67a26, ERC1155MetadataURI: 0x0e89341c.
result := or(or(eq(s, 0x01ffc9a7), eq(s, 0xd9b67a26)), eq(s, 0x0e89341c))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL MINT FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Mints `amount` of `id` to `to`.
///
/// Requirements:
/// - `to` cannot be the zero address.
/// - If `to` refers to a smart contract, it must implement
/// {ERC1155-onERC1155Received}, which is called upon a batch transfer.
///
/// Emits a {TransferSingle} event.
function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual {
if (_useBeforeTokenTransfer()) {
_beforeTokenTransfer(address(0), to, _single(id), _single(amount), data);
}
/// @solidity memory-safe-assembly
assembly {
let to_ := shl(96, to)
// Revert if `to` is the zero address.
if iszero(to_) {
mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
revert(0x1c, 0x04)
}
// Increase and store the updated balance of `to`.
{
mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
mstore(0x14, to)
mstore(0x00, id)
let toBalanceSlot := keccak256(0x00, 0x40)
let toBalanceBefore := sload(toBalanceSlot)
let toBalanceAfter := add(toBalanceBefore, amount)
if lt(toBalanceAfter, toBalanceBefore) {
mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
revert(0x1c, 0x04)
}
sstore(toBalanceSlot, toBalanceAfter)
}
// Emit a {TransferSingle} event.
mstore(0x20, amount)
log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), 0, shr(96, to_))
}
if (_useAfterTokenTransfer()) {
_afterTokenTransfer(address(0), to, _single(id), _single(amount), data);
}
if (_hasCode(to)) _checkOnERC1155Received(address(0), to, id, amount, data);
}
/// @dev Mints `amounts` of `ids` to `to`.
///
/// Requirements:
/// - `to` cannot be the zero address.
/// - `ids` and `amounts` must have the same length.
/// - If `to` refers to a smart contract, it must implement
/// {ERC1155-onERC1155BatchReceived}, which is called upon a batch transfer.
///
/// Emits a {TransferBatch} event.
function _batchMint(
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {
if (_useBeforeTokenTransfer()) {
_beforeTokenTransfer(address(0), to, ids, amounts, data);
}
/// @solidity memory-safe-assembly
assembly {
if iszero(eq(mload(ids), mload(amounts))) {
mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
revert(0x1c, 0x04)
}
let to_ := shl(96, to)
// Revert if `to` is the zero address.
if iszero(to_) {
mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
revert(0x1c, 0x04)
}
// Loop through all the `ids` and update the balances.
{
mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, to_))
for { let i := shl(5, mload(ids)) } i { i := sub(i, 0x20) } {
let amount := mload(add(amounts, i))
// Increase and store the updated balance of `to`.
{
mstore(0x00, mload(add(ids, i)))
let toBalanceSlot := keccak256(0x00, 0x40)
let toBalanceBefore := sload(toBalanceSlot)
let toBalanceAfter := add(toBalanceBefore, amount)
if lt(toBalanceAfter, toBalanceBefore) {
mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
revert(0x1c, 0x04)
}
sstore(toBalanceSlot, toBalanceAfter)
}
}
}
// Emit a {TransferBatch} event.
{
let m := mload(0x40)
// Copy the `ids`.
mstore(m, 0x40)
let n := add(0x20, shl(5, mload(ids)))
let o := add(m, 0x40)
pop(staticcall(gas(), 4, ids, n, o, n))
// Copy the `amounts`.
mstore(add(m, 0x20), add(0x40, returndatasize()))
o := add(o, returndatasize())
n := add(0x20, shl(5, mload(amounts)))
pop(staticcall(gas(), 4, amounts, n, o, n))
n := sub(add(o, returndatasize()), m)
// Do the emit.
log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), 0, shr(96, to_))
}
}
if (_useAfterTokenTransfer()) {
_afterTokenTransfer(address(0), to, ids, amounts, data);
}
if (_hasCode(to)) _checkOnERC1155BatchReceived(address(0), to, ids, amounts, data);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL BURN FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to `_burn(address(0), from, id, amount)`.
function _burn(address from, uint256 id, uint256 amount) internal virtual {
_burn(address(0), from, id, amount);
}
/// @dev Destroys `amount` of `id` from `from`.
///
/// Requirements:
/// - `from` must have at least `amount` of `id`.
/// - If `by` is not the zero address, it must be either `from`,
/// or approved to manage the tokens of `from`.
///
/// Emits a {TransferSingle} event.
function _burn(address by, address from, uint256 id, uint256 amount) internal virtual {
if (_useBeforeTokenTransfer()) {
_beforeTokenTransfer(from, address(0), _single(id), _single(amount), "");
}
/// @solidity memory-safe-assembly
assembly {
let from_ := shl(96, from)
mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_))
// If `by` is not the zero address, and not equal to `from`,
// check if it is approved to manage all the tokens of `from`.
if iszero(or(iszero(shl(96, by)), eq(shl(96, by), from_))) {
mstore(0x00, by)
if iszero(sload(keccak256(0x0c, 0x34))) {
mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
revert(0x1c, 0x04)
}
}
// Decrease and store the updated balance of `from`.
{
mstore(0x00, id)
let fromBalanceSlot := keccak256(0x00, 0x40)
let fromBalance := sload(fromBalanceSlot)
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
sstore(fromBalanceSlot, sub(fromBalance, amount))
}
// Emit a {TransferSingle} event.
mstore(0x20, amount)
log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), shr(96, from_), 0)
}
if (_useAfterTokenTransfer()) {
_afterTokenTransfer(from, address(0), _single(id), _single(amount), "");
}
}
/// @dev Equivalent to `_batchBurn(address(0), from, ids, amounts)`.
function _batchBurn(address from, uint256[] memory ids, uint256[] memory amounts)
internal
virtual
{
_batchBurn(address(0), from, ids, amounts);
}
/// @dev Destroys `amounts` of `ids` from `from`.
///
/// Requirements:
/// - `ids` and `amounts` must have the same length.
/// - `from` must have at least `amounts` of `ids`.
/// - If `by` is not the zero address, it must be either `from`,
/// or approved to manage the tokens of `from`.
///
/// Emits a {TransferBatch} event.
function _batchBurn(address by, address from, uint256[] memory ids, uint256[] memory amounts)
internal
virtual
{
if (_useBeforeTokenTransfer()) {
_beforeTokenTransfer(from, address(0), ids, amounts, "");
}
/// @solidity memory-safe-assembly
assembly {
if iszero(eq(mload(ids), mload(amounts))) {
mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
revert(0x1c, 0x04)
}
let from_ := shl(96, from)
mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_))
// If `by` is not the zero address, and not equal to `from`,
// check if it is approved to manage all the tokens of `from`.
let by_ := shl(96, by)
if iszero(or(iszero(by_), eq(by_, from_))) {
mstore(0x00, by)
if iszero(sload(keccak256(0x0c, 0x34))) {
mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
revert(0x1c, 0x04)
}
}
// Loop through all the `ids` and update the balances.
{
for { let i := shl(5, mload(ids)) } i { i := sub(i, 0x20) } {
let amount := mload(add(amounts, i))
// Decrease and store the updated balance of `from`.
{
mstore(0x00, mload(add(ids, i)))
let fromBalanceSlot := keccak256(0x00, 0x40)
let fromBalance := sload(fromBalanceSlot)
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
sstore(fromBalanceSlot, sub(fromBalance, amount))
}
}
}
// Emit a {TransferBatch} event.
{
let m := mload(0x40)
// Copy the `ids`.
mstore(m, 0x40)
let n := add(0x20, shl(5, mload(ids)))
let o := add(m, 0x40)
pop(staticcall(gas(), 4, ids, n, o, n))
// Copy the `amounts`.
mstore(add(m, 0x20), add(0x40, returndatasize()))
o := add(o, returndatasize())
n := add(0x20, shl(5, mload(amounts)))
pop(staticcall(gas(), 4, amounts, n, o, n))
n := sub(add(o, returndatasize()), m)
// Do the emit.
log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), shr(96, from_), 0)
}
}
if (_useAfterTokenTransfer()) {
_afterTokenTransfer(from, address(0), ids, amounts, "");
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL APPROVAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Approve or remove the `operator` as an operator for `by`,
/// without authorization checks.
///
/// Emits a {ApprovalForAll} event.
function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual {
/// @solidity memory-safe-assembly
assembly {
// Convert to 0 or 1.
isApproved := iszero(iszero(isApproved))
// Update the `isApproved` for (`by`, `operator`).
mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
mstore(0x14, by)
mstore(0x00, operator)
sstore(keccak256(0x0c, 0x34), isApproved)
// Emit the {ApprovalForAll} event.
mstore(0x00, isApproved)
let m := shr(96, not(0))
log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, and(m, by), and(m, operator))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL TRANSFER FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to `_safeTransfer(address(0), from, to, id, amount, data)`.
function _safeTransfer(address from, address to, uint256 id, uint256 amount, bytes memory data)
internal
virtual
{
_safeTransfer(address(0), from, to, id, amount, data);
}
/// @dev Transfers `amount` of `id` from `from` to `to`.
///
/// Requirements:
/// - `to` cannot be the zero address.
/// - `from` must have at least `amount` of `id`.
/// - If `by` is not the zero address, it must be either `from`,
/// or approved to manage the tokens of `from`.
/// - If `to` refers to a smart contract, it must implement
/// {ERC1155-onERC1155Received}, which is called upon a batch transfer.
///
/// Emits a {TransferSingle} event.
function _safeTransfer(
address by,
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) internal virtual {
if (_useBeforeTokenTransfer()) {
_beforeTokenTransfer(from, to, _single(id), _single(amount), data);
}
/// @solidity memory-safe-assembly
assembly {
let from_ := shl(96, from)
let to_ := shl(96, to)
// Revert if `to` is the zero address.
if iszero(to_) {
mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
revert(0x1c, 0x04)
}
mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_))
// If `by` is not the zero address, and not equal to `from`,
// check if it is approved to manage all the tokens of `from`.
let by_ := shl(96, by)
if iszero(or(iszero(by_), eq(by_, from_))) {
mstore(0x00, by)
if iszero(sload(keccak256(0x0c, 0x34))) {
mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
revert(0x1c, 0x04)
}
}
// Subtract and store the updated balance of `from`.
{
mstore(0x00, id)
let fromBalanceSlot := keccak256(0x00, 0x40)
let fromBalance := sload(fromBalanceSlot)
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
sstore(fromBalanceSlot, sub(fromBalance, amount))
}
// Increase and store the updated balance of `to`.
{
mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, to_))
let toBalanceSlot := keccak256(0x00, 0x40)
let toBalanceBefore := sload(toBalanceSlot)
let toBalanceAfter := add(toBalanceBefore, amount)
if lt(toBalanceAfter, toBalanceBefore) {
mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
revert(0x1c, 0x04)
}
sstore(toBalanceSlot, toBalanceAfter)
}
// Emit a {TransferSingle} event.
mstore(0x20, amount)
// forgefmt: disable-next-line
log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), shr(96, from_), shr(96, to_))
}
if (_useAfterTokenTransfer()) {
_afterTokenTransfer(from, to, _single(id), _single(amount), data);
}
if (_hasCode(to)) _checkOnERC1155Received(from, to, id, amount, data);
}
/// @dev Equivalent to `_safeBatchTransfer(address(0), from, to, ids, amounts, data)`.
function _safeBatchTransfer(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {
_safeBatchTransfer(address(0), from, to, ids, amounts, data);
}
/// @dev Transfers `amounts` of `ids` from `from` to `to`.
///
/// Requirements:
/// - `to` cannot be the zero address.
/// - `ids` and `amounts` must have the same length.
/// - `from` must have at least `amounts` of `ids`.
/// - If `by` is not the zero address, it must be either `from`,
/// or approved to manage the tokens of `from`.
/// - If `to` refers to a smart contract, it must implement
/// {ERC1155-onERC1155BatchReceived}, which is called upon a batch transfer.
///
/// Emits a {TransferBatch} event.
function _safeBatchTransfer(
address by,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {
if (_useBeforeTokenTransfer()) {
_beforeTokenTransfer(from, to, ids, amounts, data);
}
/// @solidity memory-safe-assembly
assembly {
if iszero(eq(mload(ids), mload(amounts))) {
mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
revert(0x1c, 0x04)
}
let from_ := shl(96, from)
let to_ := shl(96, to)
// Revert if `to` is the zero address.
if iszero(to_) {
mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
revert(0x1c, 0x04)
}
let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, from_)
let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, to_)
mstore(0x20, fromSlotSeed)
// If `by` is not the zero address, and not equal to `from`,
// check if it is approved to manage all the tokens of `from`.
let by_ := shl(96, by)
if iszero(or(iszero(by_), eq(by_, from_))) {
mstore(0x00, by)
if iszero(sload(keccak256(0x0c, 0x34))) {
mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
revert(0x1c, 0x04)
}
}
// Loop through all the `ids` and update the balances.
{
for { let i := shl(5, mload(ids)) } i { i := sub(i, 0x20) } {
let amount := mload(add(amounts, i))
// Subtract and store the updated balance of `from`.
{
mstore(0x20, fromSlotSeed)
mstore(0x00, mload(add(ids, i)))
let fromBalanceSlot := keccak256(0x00, 0x40)
let fromBalance := sload(fromBalanceSlot)
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
sstore(fromBalanceSlot, sub(fromBalance, amount))
}
// Increase and store the updated balance of `to`.
{
mstore(0x20, toSlotSeed)
let toBalanceSlot := keccak256(0x00, 0x40)
let toBalanceBefore := sload(toBalanceSlot)
let toBalanceAfter := add(toBalanceBefore, amount)
if lt(toBalanceAfter, toBalanceBefore) {
mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
revert(0x1c, 0x04)
}
sstore(toBalanceSlot, toBalanceAfter)
}
}
}
// Emit a {TransferBatch} event.
{
let m := mload(0x40)
// Copy the `ids`.
mstore(m, 0x40)
let n := add(0x20, shl(5, mload(ids)))
let o := add(m, 0x40)
pop(staticcall(gas(), 4, ids, n, o, n))
// Copy the `amounts`.
mstore(add(m, 0x20), add(0x40, returndatasize()))
o := add(o, returndatasize())
n := add(0x20, shl(5, mload(amounts)))
pop(staticcall(gas(), 4, amounts, n, o, n))
n := sub(add(o, returndatasize()), m)
// Do the emit.
log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), shr(96, from_), shr(96, to_))
}
}
if (_useAfterTokenTransfer()) {
_afterTokenTransfer(from, to, ids, amounts, data);
}
if (_hasCode(to)) _checkOnERC1155BatchReceived(from, to, ids, amounts, data);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HOOKS FOR OVERRIDING */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override this function to return true if `_beforeTokenTransfer` is used.
/// This is to help the compiler avoid producing dead bytecode.
function _useBeforeTokenTransfer() internal view virtual returns (bool) {
return false;
}
/// @dev Hook that is called before any token transfer.
/// This includes minting and burning, as well as batched variants.
///
/// The same hook is called on both single and batched variants.
/// For single transfers, the length of the `id` and `amount` arrays are 1.
function _beforeTokenTransfer(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {}
/// @dev Override this function to return true if `_afterTokenTransfer` is used.
/// This is to help the compiler avoid producing dead bytecode.
function _useAfterTokenTransfer() internal view virtual returns (bool) {
return false;
}
/// @dev Hook that is called after any token transfer.
/// This includes minting and burning, as well as batched variants.
///
/// The same hook is called on both single and batched variants.
/// For single transfers, the length of the `id` and `amount` arrays are 1.
function _afterTokenTransfer(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Helper for calling the `_afterTokenTransfer` hook.
/// This is to help the compiler avoid producing dead bytecode.
function _afterTokenTransferCalldata(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) private {
if (_useAfterTokenTransfer()) {
_afterTokenTransfer(from, to, ids, amounts, data);
}
}
/// @dev Returns if `a` has bytecode of non-zero length.
function _hasCode(address a) private view returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := extcodesize(a) // Can handle dirty upper bits.
}
}
/// @dev Perform a call to invoke {IERC1155Receiver-onERC1155Received} on `to`.
/// Reverts if the target does not support the function correctly.
function _checkOnERC1155Received(
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) private {
/// @solidity memory-safe-assembly
assembly {
// Prepare the calldata.
let m := mload(0x40)
// `onERC1155Received(address,address,uint256,uint256,bytes)`.
mstore(m, 0xf23a6e61)
mstore(add(m, 0x20), caller())
mstore(add(m, 0x40), shr(96, shl(96, from)))
mstore(add(m, 0x60), id)
mstore(add(m, 0x80), amount)
mstore(add(m, 0xa0), 0xa0)
let n := mload(data)
mstore(add(m, 0xc0), n)
if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xe0), n)) }
// Revert if the call reverts.
if iszero(call(gas(), to, 0, add(m, 0x1c), add(0xc4, n), m, 0x20)) {
if returndatasize() {
// Bubble up the revert if the call reverts.
returndatacopy(m, 0x00, returndatasize())
revert(m, returndatasize())
}
}
// Load the returndata and compare it with the function selector.
if iszero(eq(mload(m), shl(224, 0xf23a6e61))) {
mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Perform a call to invoke {IERC1155Receiver-onERC1155BatchReceived} on `to`.
/// Reverts if the target does not support the function correctly.
function _checkOnERC1155BatchReceived(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) private {
/// @solidity memory-safe-assembly
assembly {
// Prepare the calldata.
let m := mload(0x40)
// `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`.
mstore(m, 0xbc197c81)
mstore(add(m, 0x20), caller())
mstore(add(m, 0x40), shr(96, shl(96, from)))
// Copy the `ids`.
mstore(add(m, 0x60), 0xa0)
let n := add(0x20, shl(5, mload(ids)))
let o := add(m, 0xc0)
pop(staticcall(gas(), 4, ids, n, o, n))
// Copy the `amounts`.
let s := add(0xa0, returndatasize())
mstore(add(m, 0x80), s)
o := add(o, returndatasize())
n := add(0x20, shl(5, mload(amounts)))
pop(staticcall(gas(), 4, amounts, n, o, n))
// Copy the `data`.
mstore(add(m, 0xa0), add(s, returndatasize()))
o := add(o, returndatasize())
n := add(0x20, mload(data))
pop(staticcall(gas(), 4, data, n, o, n))
n := sub(add(o, returndatasize()), add(m, 0x1c))
// Revert if the call reverts.
if iszero(call(gas(), to, 0, add(m, 0x1c), n, m, 0x20)) {
if returndatasize() {
// Bubble up the revert if the call reverts.
returndatacopy(m, 0x00, returndatasize())
revert(m, returndatasize())
}
}
// Load the returndata and compare it with the function selector.
if iszero(eq(mload(m), shl(224, 0xbc197c81))) {
mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns `x` in an array with a single element.
function _single(uint256 x) private pure returns (uint256[] memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
mstore(0x40, add(result, 0x40))
mstore(result, 1)
mstore(add(result, 0x20), x)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {Ownable} from "./Ownable.sol";
/// @notice Simple single owner and multiroles authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/OwnableRoles.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract OwnableRoles is Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The `user`'s roles is updated to `roles`.
/// Each bit of `roles` represents whether the role is set.
event RolesUpdated(address indexed user, uint256 indexed roles);
/// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`.
uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE =
0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The role slot of `user` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED))
/// let roleSlot := keccak256(0x00, 0x20)
/// ```
/// This automatically ignores the upper bits of the `user` in case
/// they are not clean, as well as keep the `keccak256` under 32-bytes.
///
/// Note: This is equivalent to `uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))`.
uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Overwrite the roles directly without authorization guard.
function _setRoles(address user, uint256 roles) internal virtual {
/// @solidity memory-safe-assembly
assembly {
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
// Store the new value.
sstore(keccak256(0x0c, 0x20), roles)
// Emit the {RolesUpdated} event.
log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles)
}
}
/// @dev Updates the roles directly without authorization guard.
/// If `on` is true, each set bit of `roles` will be turned on,
/// otherwise, each set bit of `roles` will be turned off.
function _updateRoles(address user, uint256 roles, bool on) internal virtual {
/// @solidity memory-safe-assembly
assembly {
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
let roleSlot := keccak256(0x0c, 0x20)
// Load the current value.
let current := sload(roleSlot)
// Compute the updated roles if `on` is true.
let updated := or(current, roles)
// Compute the updated roles if `on` is false.
// Use `and` to compute the intersection of `current` and `roles`,
// `xor` it with `current` to flip the bits in the intersection.
if iszero(on) { updated := xor(current, and(current, roles)) }
// Then, store the new value.
sstore(roleSlot, updated)
// Emit the {RolesUpdated} event.
log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), updated)
}
}
/// @dev Grants the roles directly without authorization guard.
/// Each bit of `roles` represents the role to turn on.
function _grantRoles(address user, uint256 roles) internal virtual {
_updateRoles(user, roles, true);
}
/// @dev Removes the roles directly without authorization guard.
/// Each bit of `roles` represents the role to turn off.
function _removeRoles(address user, uint256 roles) internal virtual {
_updateRoles(user, roles, false);
}
/// @dev Throws if the sender does not have any of the `roles`.
function _checkRoles(uint256 roles) internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection
// of the value and `roles` is zero, revert.
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Throws if the sender is not the owner,
/// and does not have any of the `roles`.
/// Checks for ownership first, then lazily checks for roles.
function _checkOwnerOrRoles(uint256 roles) internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner.
// Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection
// of the value and `roles` is zero, revert.
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Throws if the sender does not have any of the `roles`,
/// and is not the owner.
/// Checks for roles first, then lazily checks for ownership.
function _checkRolesOrOwner(uint256 roles) internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection
// of the value and `roles` is zero, revert.
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
// If the caller is not the stored owner.
// Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`.
/// This is meant for frontends like Etherscan, and is therefore not fully optimized.
/// Not recommended to be called on-chain.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _rolesFromOrdinals(uint8[] memory ordinals) internal pure returns (uint256 roles) {
/// @solidity memory-safe-assembly
assembly {
for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } {
// We don't need to mask the values of `ordinals`, as Solidity
// cleans dirty upper bits when storing variables into memory.
roles := or(shl(mload(add(ordinals, i)), 1), roles)
}
}
}
/// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap.
/// This is meant for frontends like Etherscan, and is therefore not fully optimized.
/// Not recommended to be called on-chain.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ordinalsFromRoles(uint256 roles) internal pure returns (uint8[] memory ordinals) {
/// @solidity memory-safe-assembly
assembly {
// Grab the pointer to the free memory.
ordinals := mload(0x40)
let ptr := add(ordinals, 0x20)
let o := 0
// The absence of lookup tables, De Bruijn, etc., here is intentional for
// smaller bytecode, as this function is not meant to be called on-chain.
for { let t := roles } 1 {} {
mstore(ptr, o)
// `shr` 5 is equivalent to multiplying by 0x20.
// Push back into the ordinals array if the bit is set.
ptr := add(ptr, shl(5, and(t, 1)))
o := add(o, 1)
t := shr(o, roles)
if iszero(t) { break }
}
// Store the length of `ordinals`.
mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20))))
// Allocate the memory.
mstore(0x40, ptr)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to grant `user` `roles`.
/// If the `user` already has a role, then it will be an no-op for the role.
function grantRoles(address user, uint256 roles) public payable virtual onlyOwner {
_grantRoles(user, roles);
}
/// @dev Allows the owner to remove `user` `roles`.
/// If the `user` does not have a role, then it will be an no-op for the role.
function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner {
_removeRoles(user, roles);
}
/// @dev Allow the caller to remove their own roles.
/// If the caller does not have a role, then it will be an no-op for the role.
function renounceRoles(uint256 roles) public payable virtual {
_removeRoles(msg.sender, roles);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the roles of `user`.
function rolesOf(address user) public view virtual returns (uint256 roles) {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
// Load the stored value.
roles := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Returns whether `user` has any of `roles`.
function hasAnyRole(address user, uint256 roles) public view virtual returns (bool) {
return rolesOf(user) & roles != 0;
}
/// @dev Returns whether `user` has all of `roles`.
function hasAllRoles(address user, uint256 roles) public view virtual returns (bool) {
return rolesOf(user) & roles == roles;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by an account with `roles`.
modifier onlyRoles(uint256 roles) virtual {
_checkRoles(roles);
_;
}
/// @dev Marks a function as only callable by the owner or by an account
/// with `roles`. Checks for ownership first, then lazily checks for roles.
modifier onlyOwnerOrRoles(uint256 roles) virtual {
_checkOwnerOrRoles(roles);
_;
}
/// @dev Marks a function as only callable by an account with `roles`
/// or the owner. Checks for roles first, then lazily checks for ownership.
modifier onlyRolesOrOwner(uint256 roles) virtual {
_checkRolesOrOwner(roles);
_;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ROLE CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// IYKYK
uint256 internal constant _ROLE_0 = 1 << 0;
uint256 internal constant _ROLE_1 = 1 << 1;
uint256 internal constant _ROLE_2 = 1 << 2;
uint256 internal constant _ROLE_3 = 1 << 3;
uint256 internal constant _ROLE_4 = 1 << 4;
uint256 internal constant _ROLE_5 = 1 << 5;
uint256 internal constant _ROLE_6 = 1 << 6;
uint256 internal constant _ROLE_7 = 1 << 7;
uint256 internal constant _ROLE_8 = 1 << 8;
uint256 internal constant _ROLE_9 = 1 << 9;
uint256 internal constant _ROLE_10 = 1 << 10;
uint256 internal constant _ROLE_11 = 1 << 11;
uint256 internal constant _ROLE_12 = 1 << 12;
uint256 internal constant _ROLE_13 = 1 << 13;
uint256 internal constant _ROLE_14 = 1 << 14;
uint256 internal constant _ROLE_15 = 1 << 15;
uint256 internal constant _ROLE_16 = 1 << 16;
uint256 internal constant _ROLE_17 = 1 << 17;
uint256 internal constant _ROLE_18 = 1 << 18;
uint256 internal constant _ROLE_19 = 1 << 19;
uint256 internal constant _ROLE_20 = 1 << 20;
uint256 internal constant _ROLE_21 = 1 << 21;
uint256 internal constant _ROLE_22 = 1 << 22;
uint256 internal constant _ROLE_23 = 1 << 23;
uint256 internal constant _ROLE_24 = 1 << 24;
uint256 internal constant _ROLE_25 = 1 << 25;
uint256 internal constant _ROLE_26 = 1 << 26;
uint256 internal constant _ROLE_27 = 1 << 27;
uint256 internal constant _ROLE_28 = 1 << 28;
uint256 internal constant _ROLE_29 = 1 << 29;
uint256 internal constant _ROLE_30 = 1 << 30;
uint256 internal constant _ROLE_31 = 1 << 31;
uint256 internal constant _ROLE_32 = 1 << 32;
uint256 internal constant _ROLE_33 = 1 << 33;
uint256 internal constant _ROLE_34 = 1 << 34;
uint256 internal constant _ROLE_35 = 1 << 35;
uint256 internal constant _ROLE_36 = 1 << 36;
uint256 internal constant _ROLE_37 = 1 << 37;
uint256 internal constant _ROLE_38 = 1 << 38;
uint256 internal constant _ROLE_39 = 1 << 39;
uint256 internal constant _ROLE_40 = 1 << 40;
uint256 internal constant _ROLE_41 = 1 << 41;
uint256 internal constant _ROLE_42 = 1 << 42;
uint256 internal constant _ROLE_43 = 1 << 43;
uint256 internal constant _ROLE_44 = 1 << 44;
uint256 internal constant _ROLE_45 = 1 << 45;
uint256 internal constant _ROLE_46 = 1 << 46;
uint256 internal constant _ROLE_47 = 1 << 47;
uint256 internal constant _ROLE_48 = 1 << 48;
uint256 internal constant _ROLE_49 = 1 << 49;
uint256 internal constant _ROLE_50 = 1 << 50;
uint256 internal constant _ROLE_51 = 1 << 51;
uint256 internal constant _ROLE_52 = 1 << 52;
uint256 internal constant _ROLE_53 = 1 << 53;
uint256 internal constant _ROLE_54 = 1 << 54;
uint256 internal constant _ROLE_55 = 1 << 55;
uint256 internal constant _ROLE_56 = 1 << 56;
uint256 internal constant _ROLE_57 = 1 << 57;
uint256 internal constant _ROLE_58 = 1 << 58;
uint256 internal constant _ROLE_59 = 1 << 59;
uint256 internal constant _ROLE_60 = 1 << 60;
uint256 internal constant _ROLE_61 = 1 << 61;
uint256 internal constant _ROLE_62 = 1 << 62;
uint256 internal constant _ROLE_63 = 1 << 63;
uint256 internal constant _ROLE_64 = 1 << 64;
uint256 internal constant _ROLE_65 = 1 << 65;
uint256 internal constant _ROLE_66 = 1 << 66;
uint256 internal constant _ROLE_67 = 1 << 67;
uint256 internal constant _ROLE_68 = 1 << 68;
uint256 internal constant _ROLE_69 = 1 << 69;
uint256 internal constant _ROLE_70 = 1 << 70;
uint256 internal constant _ROLE_71 = 1 << 71;
uint256 internal constant _ROLE_72 = 1 << 72;
uint256 internal constant _ROLE_73 = 1 << 73;
uint256 internal constant _ROLE_74 = 1 << 74;
uint256 internal constant _ROLE_75 = 1 << 75;
uint256 internal constant _ROLE_76 = 1 << 76;
uint256 internal constant _ROLE_77 = 1 << 77;
uint256 internal constant _ROLE_78 = 1 << 78;
uint256 internal constant _ROLE_79 = 1 << 79;
uint256 internal constant _ROLE_80 = 1 << 80;
uint256 internal constant _ROLE_81 = 1 << 81;
uint256 internal constant _ROLE_82 = 1 << 82;
uint256 internal constant _ROLE_83 = 1 << 83;
uint256 internal constant _ROLE_84 = 1 << 84;
uint256 internal constant _ROLE_85 = 1 << 85;
uint256 internal constant _ROLE_86 = 1 << 86;
uint256 internal constant _ROLE_87 = 1 << 87;
uint256 internal constant _ROLE_88 = 1 << 88;
uint256 internal constant _ROLE_89 = 1 << 89;
uint256 internal constant _ROLE_90 = 1 << 90;
uint256 internal constant _ROLE_91 = 1 << 91;
uint256 internal constant _ROLE_92 = 1 << 92;
uint256 internal constant _ROLE_93 = 1 << 93;
uint256 internal constant _ROLE_94 = 1 << 94;
uint256 internal constant _ROLE_95 = 1 << 95;
uint256 internal constant _ROLE_96 = 1 << 96;
uint256 internal constant _ROLE_97 = 1 << 97;
uint256 internal constant _ROLE_98 = 1 << 98;
uint256 internal constant _ROLE_99 = 1 << 99;
uint256 internal constant _ROLE_100 = 1 << 100;
uint256 internal constant _ROLE_101 = 1 << 101;
uint256 internal constant _ROLE_102 = 1 << 102;
uint256 internal constant _ROLE_103 = 1 << 103;
uint256 internal constant _ROLE_104 = 1 << 104;
uint256 internal constant _ROLE_105 = 1 << 105;
uint256 internal constant _ROLE_106 = 1 << 106;
uint256 internal constant _ROLE_107 = 1 << 107;
uint256 internal constant _ROLE_108 = 1 << 108;
uint256 internal constant _ROLE_109 = 1 << 109;
uint256 internal constant _ROLE_110 = 1 << 110;
uint256 internal constant _ROLE_111 = 1 << 111;
uint256 internal constant _ROLE_112 = 1 << 112;
uint256 internal constant _ROLE_113 = 1 << 113;
uint256 internal constant _ROLE_114 = 1 << 114;
uint256 internal constant _ROLE_115 = 1 << 115;
uint256 internal constant _ROLE_116 = 1 << 116;
uint256 internal constant _ROLE_117 = 1 << 117;
uint256 internal constant _ROLE_118 = 1 << 118;
uint256 internal constant _ROLE_119 = 1 << 119;
uint256 internal constant _ROLE_120 = 1 << 120;
uint256 internal constant _ROLE_121 = 1 << 121;
uint256 internal constant _ROLE_122 = 1 << 122;
uint256 internal constant _ROLE_123 = 1 << 123;
uint256 internal constant _ROLE_124 = 1 << 124;
uint256 internal constant _ROLE_125 = 1 << 125;
uint256 internal constant _ROLE_126 = 1 << 126;
uint256 internal constant _ROLE_127 = 1 << 127;
uint256 internal constant _ROLE_128 = 1 << 128;
uint256 internal constant _ROLE_129 = 1 << 129;
uint256 internal constant _ROLE_130 = 1 << 130;
uint256 internal constant _ROLE_131 = 1 << 131;
uint256 internal constant _ROLE_132 = 1 << 132;
uint256 internal constant _ROLE_133 = 1 << 133;
uint256 internal constant _ROLE_134 = 1 << 134;
uint256 internal constant _ROLE_135 = 1 << 135;
uint256 internal constant _ROLE_136 = 1 << 136;
uint256 internal constant _ROLE_137 = 1 << 137;
uint256 internal constant _ROLE_138 = 1 << 138;
uint256 internal constant _ROLE_139 = 1 << 139;
uint256 internal constant _ROLE_140 = 1 << 140;
uint256 internal constant _ROLE_141 = 1 << 141;
uint256 internal constant _ROLE_142 = 1 << 142;
uint256 internal constant _ROLE_143 = 1 << 143;
uint256 internal constant _ROLE_144 = 1 << 144;
uint256 internal constant _ROLE_145 = 1 << 145;
uint256 internal constant _ROLE_146 = 1 << 146;
uint256 internal constant _ROLE_147 = 1 << 147;
uint256 internal constant _ROLE_148 = 1 << 148;
uint256 internal constant _ROLE_149 = 1 << 149;
uint256 internal constant _ROLE_150 = 1 << 150;
uint256 internal constant _ROLE_151 = 1 << 151;
uint256 internal constant _ROLE_152 = 1 << 152;
uint256 internal constant _ROLE_153 = 1 << 153;
uint256 internal constant _ROLE_154 = 1 << 154;
uint256 internal constant _ROLE_155 = 1 << 155;
uint256 internal constant _ROLE_156 = 1 << 156;
uint256 internal constant _ROLE_157 = 1 << 157;
uint256 internal constant _ROLE_158 = 1 << 158;
uint256 internal constant _ROLE_159 = 1 << 159;
uint256 internal constant _ROLE_160 = 1 << 160;
uint256 internal constant _ROLE_161 = 1 << 161;
uint256 internal constant _ROLE_162 = 1 << 162;
uint256 internal constant _ROLE_163 = 1 << 163;
uint256 internal constant _ROLE_164 = 1 << 164;
uint256 internal constant _ROLE_165 = 1 << 165;
uint256 internal constant _ROLE_166 = 1 << 166;
uint256 internal constant _ROLE_167 = 1 << 167;
uint256 internal constant _ROLE_168 = 1 << 168;
uint256 internal constant _ROLE_169 = 1 << 169;
uint256 internal constant _ROLE_170 = 1 << 170;
uint256 internal constant _ROLE_171 = 1 << 171;
uint256 internal constant _ROLE_172 = 1 << 172;
uint256 internal constant _ROLE_173 = 1 << 173;
uint256 internal constant _ROLE_174 = 1 << 174;
uint256 internal constant _ROLE_175 = 1 << 175;
uint256 internal constant _ROLE_176 = 1 << 176;
uint256 internal constant _ROLE_177 = 1 << 177;
uint256 internal constant _ROLE_178 = 1 << 178;
uint256 internal constant _ROLE_179 = 1 << 179;
uint256 internal constant _ROLE_180 = 1 << 180;
uint256 internal constant _ROLE_181 = 1 << 181;
uint256 internal constant _ROLE_182 = 1 << 182;
uint256 internal constant _ROLE_183 = 1 << 183;
uint256 internal constant _ROLE_184 = 1 << 184;
uint256 internal constant _ROLE_185 = 1 << 185;
uint256 internal constant _ROLE_186 = 1 << 186;
uint256 internal constant _ROLE_187 = 1 << 187;
uint256 internal constant _ROLE_188 = 1 << 188;
uint256 internal constant _ROLE_189 = 1 << 189;
uint256 internal constant _ROLE_190 = 1 << 190;
uint256 internal constant _ROLE_191 = 1 << 191;
uint256 internal constant _ROLE_192 = 1 << 192;
uint256 internal constant _ROLE_193 = 1 << 193;
uint256 internal constant _ROLE_194 = 1 << 194;
uint256 internal constant _ROLE_195 = 1 << 195;
uint256 internal constant _ROLE_196 = 1 << 196;
uint256 internal constant _ROLE_197 = 1 << 197;
uint256 internal constant _ROLE_198 = 1 << 198;
uint256 internal constant _ROLE_199 = 1 << 199;
uint256 internal constant _ROLE_200 = 1 << 200;
uint256 internal constant _ROLE_201 = 1 << 201;
uint256 internal constant _ROLE_202 = 1 << 202;
uint256 internal constant _ROLE_203 = 1 << 203;
uint256 internal constant _ROLE_204 = 1 << 204;
uint256 internal constant _ROLE_205 = 1 << 205;
uint256 internal constant _ROLE_206 = 1 << 206;
uint256 internal constant _ROLE_207 = 1 << 207;
uint256 internal constant _ROLE_208 = 1 << 208;
uint256 internal constant _ROLE_209 = 1 << 209;
uint256 internal constant _ROLE_210 = 1 << 210;
uint256 internal constant _ROLE_211 = 1 << 211;
uint256 internal constant _ROLE_212 = 1 << 212;
uint256 internal constant _ROLE_213 = 1 << 213;
uint256 internal constant _ROLE_214 = 1 << 214;
uint256 internal constant _ROLE_215 = 1 << 215;
uint256 internal constant _ROLE_216 = 1 << 216;
uint256 internal constant _ROLE_217 = 1 << 217;
uint256 internal constant _ROLE_218 = 1 << 218;
uint256 internal constant _ROLE_219 = 1 << 219;
uint256 internal constant _ROLE_220 = 1 << 220;
uint256 internal constant _ROLE_221 = 1 << 221;
uint256 internal constant _ROLE_222 = 1 << 222;
uint256 internal constant _ROLE_223 = 1 << 223;
uint256 internal constant _ROLE_224 = 1 << 224;
uint256 internal constant _ROLE_225 = 1 << 225;
uint256 internal constant _ROLE_226 = 1 << 226;
uint256 internal constant _ROLE_227 = 1 << 227;
uint256 internal constant _ROLE_228 = 1 << 228;
uint256 internal constant _ROLE_229 = 1 << 229;
uint256 internal constant _ROLE_230 = 1 << 230;
uint256 internal constant _ROLE_231 = 1 << 231;
uint256 internal constant _ROLE_232 = 1 << 232;
uint256 internal constant _ROLE_233 = 1 << 233;
uint256 internal constant _ROLE_234 = 1 << 234;
uint256 internal constant _ROLE_235 = 1 << 235;
uint256 internal constant _ROLE_236 = 1 << 236;
uint256 internal constant _ROLE_237 = 1 << 237;
uint256 internal constant _ROLE_238 = 1 << 238;
uint256 internal constant _ROLE_239 = 1 << 239;
uint256 internal constant _ROLE_240 = 1 << 240;
uint256 internal constant _ROLE_241 = 1 << 241;
uint256 internal constant _ROLE_242 = 1 << 242;
uint256 internal constant _ROLE_243 = 1 << 243;
uint256 internal constant _ROLE_244 = 1 << 244;
uint256 internal constant _ROLE_245 = 1 << 245;
uint256 internal constant _ROLE_246 = 1 << 246;
uint256 internal constant _ROLE_247 = 1 << 247;
uint256 internal constant _ROLE_248 = 1 << 248;
uint256 internal constant _ROLE_249 = 1 << 249;
uint256 internal constant _ROLE_250 = 1 << 250;
uint256 internal constant _ROLE_251 = 1 << 251;
uint256 internal constant _ROLE_252 = 1 << 252;
uint256 internal constant _ROLE_253 = 1 << 253;
uint256 internal constant _ROLE_254 = 1 << 254;
uint256 internal constant _ROLE_255 = 1 << 255;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple ERC2981 NFT Royalty Standard implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC2981.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/common/ERC2981.sol)
abstract contract ERC2981 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The royalty fee numerator exceeds the fee denominator.
error RoyaltyOverflow();
/// @dev The royalty receiver cannot be the zero address.
error RoyaltyReceiverIsZeroAddress();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The default royalty info is given by:
/// ```
/// let packed := sload(_ERC2981_MASTER_SLOT_SEED)
/// let receiver := shr(96, packed)
/// let royaltyFraction := xor(packed, shl(96, receiver))
/// ```
///
/// The per token royalty info is given by.
/// ```
/// mstore(0x00, tokenId)
/// mstore(0x20, _ERC2981_MASTER_SLOT_SEED)
/// let packed := sload(keccak256(0x00, 0x40))
/// let receiver := shr(96, packed)
/// let royaltyFraction := xor(packed, shl(96, receiver))
/// ```
uint256 private constant _ERC2981_MASTER_SLOT_SEED = 0xaa4ec00224afccfdb7;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC2981 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Checks that `_feeDenominator` is non-zero.
constructor() {
require(_feeDenominator() != 0, "Fee denominator cannot be zero.");
}
/// @dev Returns the denominator for the royalty amount.
/// Defaults to 10000, which represents fees in basis points.
/// Override this function to return a custom amount if needed.
function _feeDenominator() internal pure virtual returns (uint96) {
return 10000;
}
/// @dev Returns true if this contract implements the interface defined by `interfaceId`.
/// See: https://eips.ethereum.org/EIPS/eip-165
/// This function call must use less than 30000 gas.
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
let s := shr(224, interfaceId)
// ERC165: 0x01ffc9a7, ERC2981: 0x2a55205a.
result := or(eq(s, 0x01ffc9a7), eq(s, 0x2a55205a))
}
}
/// @dev Returns the `receiver` and `royaltyAmount` for `tokenId` sold at `salePrice`.
function royaltyInfo(uint256 tokenId, uint256 salePrice)
public
view
virtual
returns (address receiver, uint256 royaltyAmount)
{
uint256 feeDenominator = _feeDenominator();
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, tokenId)
mstore(0x20, _ERC2981_MASTER_SLOT_SEED)
let packed := sload(keccak256(0x00, 0x40))
receiver := shr(96, packed)
if iszero(receiver) {
packed := sload(mload(0x20))
receiver := shr(96, packed)
}
let x := salePrice
let y := xor(packed, shl(96, receiver)) // `feeNumerator`.
// Overflow check, equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
// Out-of-gas revert. Should not be triggered in practice, but included for safety.
returndatacopy(returndatasize(), returndatasize(), mul(y, gt(x, div(not(0), y))))
royaltyAmount := div(mul(x, y), feeDenominator)
}
}
/// @dev Sets the default royalty `receiver` and `feeNumerator`.
///
/// Requirements:
/// - `receiver` must not be the zero address.
/// - `feeNumerator` must not be greater than the fee denominator.
function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
uint256 feeDenominator = _feeDenominator();
/// @solidity memory-safe-assembly
assembly {
feeNumerator := shr(160, shl(160, feeNumerator))
if gt(feeNumerator, feeDenominator) {
mstore(0x00, 0x350a88b3) // `RoyaltyOverflow()`.
revert(0x1c, 0x04)
}
let packed := shl(96, receiver)
if iszero(packed) {
mstore(0x00, 0xb4457eaa) // `RoyaltyReceiverIsZeroAddress()`.
revert(0x1c, 0x04)
}
sstore(_ERC2981_MASTER_SLOT_SEED, or(packed, feeNumerator))
}
}
/// @dev Sets the default royalty `receiver` and `feeNumerator` to zero.
function _deleteDefaultRoyalty() internal virtual {
/// @solidity memory-safe-assembly
assembly {
sstore(_ERC2981_MASTER_SLOT_SEED, 0)
}
}
/// @dev Sets the royalty `receiver` and `feeNumerator` for `tokenId`.
///
/// Requirements:
/// - `receiver` must not be the zero address.
/// - `feeNumerator` must not be greater than the fee denominator.
function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator)
internal
virtual
{
uint256 feeDenominator = _feeDenominator();
/// @solidity memory-safe-assembly
assembly {
feeNumerator := shr(160, shl(160, feeNumerator))
if gt(feeNumerator, feeDenominator) {
mstore(0x00, 0x350a88b3) // `RoyaltyOverflow()`.
revert(0x1c, 0x04)
}
let packed := shl(96, receiver)
if iszero(packed) {
mstore(0x00, 0xb4457eaa) // `RoyaltyReceiverIsZeroAddress()`.
revert(0x1c, 0x04)
}
mstore(0x00, tokenId)
mstore(0x20, _ERC2981_MASTER_SLOT_SEED)
sstore(keccak256(0x00, 0x40), or(packed, feeNumerator))
}
}
/// @dev Sets the royalty `receiver` and `feeNumerator` for `tokenId` to zero.
function _resetTokenRoyalty(uint256 tokenId) internal virtual {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, tokenId)
mstore(0x20, _ERC2981_MASTER_SLOT_SEED)
sstore(keccak256(0x00, 0x40), 0)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Contract for EIP-712 typed structured data hashing and signing.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol)
/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol)
///
/// @dev Note, this implementation:
/// - Uses `address(this)` for the `verifyingContract` field.
/// - Does NOT use the optional EIP-712 salt.
/// - Does NOT use any EIP-712 extensions.
/// This is for simplicity and to save gas.
/// If you need to customize, please fork / modify accordingly.
abstract contract EIP712 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS AND IMMUTABLES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
bytes32 internal constant _DOMAIN_TYPEHASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
/// @dev `keccak256("EIP712Domain(string name,string version,address verifyingContract)")`.
/// This is only used in `_hashTypedDataSansChainId`.
bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID =
0x91ab3d17e3a50a9d89e63fd30b92be7f5336b03b287bb946787a83a9d62a2766;
/// @dev `keccak256("EIP712Domain(string name,string version)")`.
/// This is only used in `_hashTypedDataSansChainIdAndVerifyingContract`.
bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT =
0xb03948446334eb9b2196d5eb166f69b9d49403eb4a12f36de8d3f9f3cb8e15c3;
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId)")`.
/// This is only used in `_hashTypedDataSansVerifyingContract`.
bytes32 internal constant _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT =
0xc2f8787176b8ac6bf7215b4adcc1e069bf4ab82d9ab1df05a57a91d425935b6e;
uint256 private immutable _cachedThis;
uint256 private immutable _cachedChainId;
bytes32 private immutable _cachedNameHash;
bytes32 private immutable _cachedVersionHash;
bytes32 private immutable _cachedDomainSeparator;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTRUCTOR */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Cache the hashes for cheaper runtime gas costs.
/// In the case of upgradeable contracts (i.e. proxies),
/// or if the chain id changes due to a hard fork,
/// the domain separator will be seamlessly calculated on-the-fly.
constructor() {
_cachedThis = uint256(uint160(address(this)));
_cachedChainId = block.chainid;
string memory name;
string memory version;
if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion();
bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name));
bytes32 versionHash =
_domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version));
_cachedNameHash = nameHash;
_cachedVersionHash = versionHash;
bytes32 separator;
if (!_domainNameAndVersionMayChange()) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
}
_cachedDomainSeparator = separator;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* FUNCTIONS TO OVERRIDE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Please override this function to return the domain name and version.
/// ```
/// function _domainNameAndVersion()
/// internal
/// pure
/// virtual
/// returns (string memory name, string memory version)
/// {
/// name = "Solady";
/// version = "1";
/// }
/// ```
///
/// Note: If the returned result may change after the contract has been deployed,
/// you must override `_domainNameAndVersionMayChange()` to return true.
function _domainNameAndVersion()
internal
view
virtual
returns (string memory name, string memory version);
/// @dev Returns if `_domainNameAndVersion()` may change
/// after the contract has been deployed (i.e. after the constructor).
/// Default: false.
function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _domainSeparator() internal view virtual returns (bytes32 separator) {
if (_domainNameAndVersionMayChange()) {
separator = _buildDomainSeparator();
} else {
separator = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator();
}
}
/// @dev Returns the hash of the fully encoded EIP-712 message for this domain,
/// given `structHash`, as defined in
/// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
///
/// The hash can be used together with {ECDSA-recover} to obtain the signer of a message:
/// ```
/// bytes32 digest = _hashTypedData(keccak256(abi.encode(
/// keccak256("Mail(address to,string contents)"),
/// mailTo,
/// keccak256(bytes(mailContents))
/// )));
/// address signer = ECDSA.recover(digest, signature);
/// ```
function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) {
// We will use `digest` to store the domain separator to save a bit of gas.
if (_domainNameAndVersionMayChange()) {
digest = _buildDomainSeparator();
} else {
digest = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator();
}
/// @solidity memory-safe-assembly
assembly {
// Compute the digest.
mstore(0x00, 0x1901000000000000) // Store "\x19\x01".
mstore(0x1a, digest) // Store the domain separator.
mstore(0x3a, structHash) // Store the struct hash.
digest := keccak256(0x18, 0x42)
// Restore the part of the free memory slot that was overwritten.
mstore(0x3a, 0)
}
}
/// @dev Variant of `_hashTypedData` that excludes the chain ID.
/// Included for the niche use case of cross-chain workflows.
function _hashTypedDataSansChainId(bytes32 structHash)
internal
view
virtual
returns (bytes32 digest)
{
(string memory name, string memory version) = _domainNameAndVersion();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID)
mstore(0x20, keccak256(add(name, 0x20), mload(name)))
mstore(0x40, keccak256(add(version, 0x20), mload(version)))
mstore(0x60, address())
// Compute the digest.
mstore(0x20, keccak256(0x00, 0x80)) // Store the domain separator.
mstore(0x00, 0x1901) // Store "\x19\x01".
mstore(0x40, structHash) // Store the struct hash.
digest := keccak256(0x1e, 0x42)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Variant of `_hashTypedData` that excludes the chain ID and verifying contract.
/// Included for the niche use case of cross-chain and multi-verifier workflows.
function _hashTypedDataSansChainIdAndVerifyingContract(bytes32 structHash)
internal
view
virtual
returns (bytes32 digest)
{
(string memory name, string memory version) = _domainNameAndVersion();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT)
mstore(0x20, keccak256(add(name, 0x20), mload(name)))
mstore(0x40, keccak256(add(version, 0x20), mload(version)))
// Compute the digest.
mstore(0x20, keccak256(0x00, 0x60)) // Store the domain separator.
mstore(0x00, 0x1901) // Store "\x19\x01".
mstore(0x40, structHash) // Store the struct hash.
digest := keccak256(0x1e, 0x42)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Variant of `_hashTypedData` that excludes the chain ID and verifying contract.
/// Included for the niche use case of multi-verifier workflows.
function _hashTypedDataSansVerifyingContract(bytes32 structHash)
internal
view
virtual
returns (bytes32 digest)
{
(string memory name, string memory version) = _domainNameAndVersion();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(0x00, _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT)
mstore(0x20, keccak256(add(name, 0x20), mload(name)))
mstore(0x40, keccak256(add(version, 0x20), mload(version)))
mstore(0x60, chainid())
// Compute the digest.
mstore(0x20, keccak256(0x00, 0x80)) // Store the domain separator.
mstore(0x00, 0x1901) // Store "\x19\x01".
mstore(0x40, structHash) // Store the struct hash.
digest := keccak256(0x1e, 0x42)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EIP-5267 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev See: https://eips.ethereum.org/EIPS/eip-5267
function eip712Domain()
public
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
fields = hex"0f"; // `0b01111`.
(name, version) = _domainNameAndVersion();
chainId = block.chainid;
verifyingContract = address(this);
salt = salt; // `bytes32(0)`.
extensions = extensions; // `new uint256[](0)`.
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _buildDomainSeparator() private view returns (bytes32 separator) {
// We will use `separator` to store the name hash to save a bit of gas.
bytes32 versionHash;
if (_domainNameAndVersionMayChange()) {
(string memory name, string memory version) = _domainNameAndVersion();
separator = keccak256(bytes(name));
versionHash = keccak256(bytes(version));
} else {
separator = _cachedNameHash;
versionHash = _cachedVersionHash;
}
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), separator) // Name hash.
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
}
/// @dev Returns if the cached domain separator has been invalidated.
function _cachedDomainSeparatorInvalidated() private view returns (bool result) {
uint256 cachedChainId = _cachedChainId;
uint256 cachedThis = _cachedThis;
/// @solidity memory-safe-assembly
assembly {
result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis)))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Signature verification helper that supports both ECDSA signatures from EOAs
/// and ERC1271 signatures from smart contract wallets like Argent and Gnosis safe.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SignatureCheckerLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol)
///
/// @dev Note:
/// - The signature checking functions use the ecrecover precompile (0x1).
/// - The `bytes memory signature` variants use the identity precompile (0x4)
/// to copy memory internally.
/// - Unlike ECDSA signatures, contract signatures are revocable.
/// - As of Solady version 0.0.134, all `bytes signature` variants accept both
/// regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
/// See: https://eips.ethereum.org/EIPS/eip-2098
/// This is for calldata efficiency on smart accounts prevalent on L2s.
///
/// WARNING! Do NOT use signatures as unique identifiers:
/// - Use a nonce in the digest to prevent replay attacks on the same contract.
/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
/// EIP-712 also enables readable signing of typed data for better user safety.
/// This implementation does NOT check if a signature is non-malleable.
library SignatureCheckerLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIGNATURE CHECKING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns whether `signature` is valid for `signer` and `hash`.
/// If `signer.code.length == 0`, then validate with `ecrecover`, else
/// it will validate with ERC1271 on `signer`.
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature)
internal
view
returns (bool isValid)
{
if (signer == address(0)) return isValid;
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
for {} 1 {} {
if iszero(extcodesize(signer)) {
switch mload(signature)
case 64 {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
}
default { break }
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
// Copy the `signature` over.
let n := add(0x20, mload(signature))
let copied := staticcall(gas(), 4, signature, n, add(m, 0x44), n)
isValid := staticcall(gas(), signer, m, add(returndatasize(), 0x44), d, 0x20)
isValid := and(eq(mload(d), f), and(isValid, copied))
break
}
}
}
/// @dev Returns whether `signature` is valid for `signer` and `hash`.
/// If `signer.code.length == 0`, then validate with `ecrecover`, else
/// it will validate with ERC1271 on `signer`.
function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature)
internal
view
returns (bool isValid)
{
if (signer == address(0)) return isValid;
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
for {} 1 {} {
if iszero(extcodesize(signer)) {
switch signature.length
case 64 {
let vs := calldataload(add(signature.offset, 0x20))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, calldataload(signature.offset)) // `r`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
calldatacopy(0x40, signature.offset, 0x40) // `r`, `s`.
}
default { break }
mstore(0x00, hash)
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), signature.length)
// Copy the `signature` over.
calldatacopy(add(m, 0x64), signature.offset, signature.length)
isValid := staticcall(gas(), signer, m, add(signature.length, 0x64), d, 0x20)
isValid := and(eq(mload(d), f), isValid)
break
}
}
}
/// @dev Returns whether the signature (`r`, `vs`) is valid for `signer` and `hash`.
/// If `signer.code.length == 0`, then validate with `ecrecover`, else
/// it will validate with ERC1271 on `signer`.
function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
internal
view
returns (bool isValid)
{
if (signer == address(0)) return isValid;
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
for {} 1 {} {
if iszero(extcodesize(signer)) {
mstore(0x00, hash)
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, r) // `r`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), 65) // Length of the signature.
mstore(add(m, 0x64), r) // `r`.
mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`.
mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`.
isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
isValid := and(eq(mload(d), f), isValid)
break
}
}
}
/// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `signer` and `hash`.
/// If `signer.code.length == 0`, then validate with `ecrecover`, else
/// it will validate with ERC1271 on `signer`.
function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
internal
view
returns (bool isValid)
{
if (signer == address(0)) return isValid;
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
for {} 1 {} {
if iszero(extcodesize(signer)) {
mstore(0x00, hash)
mstore(0x20, and(v, 0xff)) // `v`.
mstore(0x40, r) // `r`.
mstore(0x60, s) // `s`.
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), 65) // Length of the signature.
mstore(add(m, 0x64), r) // `r`.
mstore(add(m, 0x84), s) // `s`.
mstore8(add(m, 0xa4), v) // `v`.
isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
isValid := and(eq(mload(d), f), isValid)
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1271 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: These ERC1271 operations do NOT have an ECDSA fallback.
/// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature)
internal
view
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
// Copy the `signature` over.
let n := add(0x20, mload(signature))
let copied := staticcall(gas(), 4, signature, n, add(m, 0x44), n)
isValid := staticcall(gas(), signer, m, add(returndatasize(), 0x44), d, 0x20)
isValid := and(eq(mload(d), f), and(isValid, copied))
}
}
/// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
function isValidERC1271SignatureNowCalldata(
address signer,
bytes32 hash,
bytes calldata signature
) internal view returns (bool isValid) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), signature.length)
// Copy the `signature` over.
calldatacopy(add(m, 0x64), signature.offset, signature.length)
isValid := staticcall(gas(), signer, m, add(signature.length, 0x64), d, 0x20)
isValid := and(eq(mload(d), f), isValid)
}
}
/// @dev Returns whether the signature (`r`, `vs`) is valid for `hash`
/// for an ERC1271 `signer` contract.
function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
internal
view
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), 65) // Length of the signature.
mstore(add(m, 0x64), r) // `r`.
mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`.
mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`.
isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
isValid := and(eq(mload(d), f), isValid)
}
}
/// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `hash`
/// for an ERC1271 `signer` contract.
function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
internal
view
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), 65) // Length of the signature.
mstore(add(m, 0x64), r) // `r`.
mstore(add(m, 0x84), s) // `s`.
mstore8(add(m, 0xa4), v) // `v`.
isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
isValid := and(eq(mload(d), f), isValid)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC6492 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: These ERC6492 operations now include an ECDSA fallback at the very end.
// The calldata variants are excluded for brevity.
/// @dev Returns whether `signature` is valid for `hash`.
/// If the signature is postfixed with the ERC6492 magic number, it will attempt to
/// deploy / prepare the `signer` smart account before doing a regular ERC1271 check.
/// Note: This function is NOT reentrancy safe.
/// The verifier must be deployed.
/// Otherwise, the function will return false if `signer` is not yet deployed / prepared.
/// See: https://gist.github.com/Vectorized/011d6becff6e0a73e42fe100f8d7ef04
/// With a dedicated verifier, this function is safe to use in contracts
/// that have been granted special permissions.
function isValidERC6492SignatureNowAllowSideEffects(
address signer,
bytes32 hash,
bytes memory signature
) internal returns (bool isValid) {
/// @solidity memory-safe-assembly
assembly {
function callIsValidSignature(signer_, hash_, signature_) -> _isValid {
let m_ := mload(0x40)
let f_ := shl(224, 0x1626ba7e)
mstore(m_, f_) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m_, 0x04), hash_)
let d_ := add(m_, 0x24)
mstore(d_, 0x40) // The offset of the `signature` in the calldata.
let n_ := add(0x20, mload(signature_))
let copied_ := staticcall(gas(), 4, signature_, n_, add(m_, 0x44), n_)
_isValid := staticcall(gas(), signer_, m_, add(returndatasize(), 0x44), d_, 0x20)
_isValid := and(eq(mload(d_), f_), and(_isValid, copied_))
}
let noCode := iszero(extcodesize(signer))
let n := mload(signature)
for {} 1 {} {
if iszero(eq(mload(add(signature, n)), mul(0x6492, div(not(isValid), 0xffff)))) {
if iszero(noCode) { isValid := callIsValidSignature(signer, hash, signature) }
break
}
if iszero(noCode) {
let o := add(signature, 0x20) // Signature bytes.
isValid := callIsValidSignature(signer, hash, add(o, mload(add(o, 0x40))))
if isValid { break }
}
let m := mload(0x40)
mstore(m, signer)
mstore(add(m, 0x20), hash)
pop(
call(
gas(), // Remaining gas.
0x0000bc370E4DC924F427d84e2f4B9Ec81626ba7E, // Non-reverting verifier.
0, // Send zero ETH.
m, // Start of memory.
add(returndatasize(), 0x40), // Length of calldata in memory.
staticcall(gas(), 4, add(signature, 0x20), n, add(m, 0x40), n), // 1.
0x00 // Length of returndata to write.
)
)
isValid := returndatasize()
break
}
// Do `ecrecover` fallback if `noCode && !isValid`.
for {} gt(noCode, isValid) {} {
switch n
case 64 {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
}
default { break }
let m := mload(0x40)
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
}
/// @dev Returns whether `signature` is valid for `hash`.
/// If the signature is postfixed with the ERC6492 magic number, it will attempt
/// to use a reverting verifier to deploy / prepare the `signer` smart account
/// and do a `isValidSignature` check via the reverting verifier.
/// Note: This function is reentrancy safe.
/// The reverting verifier must be deployed.
/// Otherwise, the function will return false if `signer` is not yet deployed / prepared.
/// See: https://gist.github.com/Vectorized/846a474c855eee9e441506676800a9ad
function isValidERC6492SignatureNow(address signer, bytes32 hash, bytes memory signature)
internal
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
function callIsValidSignature(signer_, hash_, signature_) -> _isValid {
let m_ := mload(0x40)
let f_ := shl(224, 0x1626ba7e)
mstore(m_, f_) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m_, 0x04), hash_)
let d_ := add(m_, 0x24)
mstore(d_, 0x40) // The offset of the `signature` in the calldata.
let n_ := add(0x20, mload(signature_))
let copied_ := staticcall(gas(), 4, signature_, n_, add(m_, 0x44), n_)
_isValid := staticcall(gas(), signer_, m_, add(returndatasize(), 0x44), d_, 0x20)
_isValid := and(eq(mload(d_), f_), and(_isValid, copied_))
}
let noCode := iszero(extcodesize(signer))
let n := mload(signature)
for {} 1 {} {
if iszero(eq(mload(add(signature, n)), mul(0x6492, div(not(isValid), 0xffff)))) {
if iszero(noCode) { isValid := callIsValidSignature(signer, hash, signature) }
break
}
if iszero(noCode) {
let o := add(signature, 0x20) // Signature bytes.
isValid := callIsValidSignature(signer, hash, add(o, mload(add(o, 0x40))))
if isValid { break }
}
let m := mload(0x40)
mstore(m, signer)
mstore(add(m, 0x20), hash)
let willBeZeroIfRevertingVerifierExists :=
call(
gas(), // Remaining gas.
0x00007bd799e4A591FeA53f8A8a3E9f931626Ba7e, // Reverting verifier.
0, // Send zero ETH.
m, // Start of memory.
add(returndatasize(), 0x40), // Length of calldata in memory.
staticcall(gas(), 4, add(signature, 0x20), n, add(m, 0x40), n), // 1.
0x00 // Length of returndata to write.
)
isValid := gt(returndatasize(), willBeZeroIfRevertingVerifierExists)
break
}
// Do `ecrecover` fallback if `noCode && !isValid`.
for {} gt(noCode, isValid) {} {
switch n
case 64 {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
}
default { break }
let m := mload(0x40)
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an Ethereum Signed Message, created from a `hash`.
/// This produces a hash corresponding to the one signed with the
/// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
/// JSON-RPC method as part of EIP-191.
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, hash) // Store into scratch space for keccak256.
mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
}
}
/// @dev Returns an Ethereum Signed Message, created from `s`.
/// This produces a hash corresponding to the one signed with the
/// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
/// JSON-RPC method as part of EIP-191.
/// Note: Supports lengths of `s` up to 999999 bytes.
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let sLength := mload(s)
let o := 0x20
mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded.
mstore(0x00, 0x00)
// Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
for { let temp := sLength } 1 {} {
o := sub(o, 1)
mstore8(o, add(48, mod(temp, 10)))
temp := div(temp, 10)
if iszero(temp) { break }
}
let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
// Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
mstore(s, sLength) // Restore the length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EMPTY CALLDATA HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an empty calldata bytes.
function emptySignature() internal pure returns (bytes calldata signature) {
/// @solidity memory-safe-assembly
assembly {
signature.length := 0
}
}
}// SPDX-License-Identifier: MIT
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);
}// SPDX-License-Identifier: MIT
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;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The caller is not authorized to call the function.
error Unauthorized();
/// @dev The `newOwner` cannot be the zero address.
error NewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.
error NoHandoverRequest();
/// @dev Cannot double-initialize.
error AlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership is transferred from `oldOwner` to `newOwner`.
/// This event is intentionally kept the same as OpenZeppelin's Ownable to be
/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
/// despite it not being as lightweight as a single argument event.
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.
event OwnershipHandoverRequested(address indexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.
event OwnershipHandoverCanceled(address indexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The owner slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
/// It is intentionally chosen to be a high value
/// to avoid collision with lower slots.
/// The choice of manual storage layout is to enable compatibility
/// with both regular and upgradeable contracts.
bytes32 internal constant _OWNER_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
/// The ownership handover slot of `newOwner` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
/// let handoverSlot := keccak256(0x00, 0x20)
/// ```
/// It stores the expiry timestamp of the two-step ownership handover.
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
/// @dev Initializes the owner directly without authorization guard.
/// This function must be called upon initialization,
/// regardless of whether the contract is upgradeable or not.
/// This is to enable generalization to both regular and upgradeable contracts,
/// and to save gas in case the initial owner is not the caller.
/// For performance reasons, this function will not check if there
/// is an existing owner.
function _initializeOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
if sload(ownerSlot) {
mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(_OWNER_SLOT, newOwner)
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
}
/// @dev Sets the owner directly without authorization guard.
function _setOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, newOwner)
}
}
}
/// @dev Throws if the sender is not the owner.
function _checkOwner() internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner, revert.
if iszero(eq(caller(), sload(_OWNER_SLOT))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.
/// Override to return a different value if needed.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
return 48 * 3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to transfer the ownership to `newOwner`.
function transferOwnership(address newOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller.
/// The request will automatically expire in 48 hours (172800 seconds) by default.
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + _ownershipHandoverValidFor();
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to `expires`.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.
function cancelOwnershipHandover() public payable virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
/// Reverts if there is no existing ownership handover requested by `pendingOwner`.
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
revert(0x1c, 0x04)
}
// Set the handover slot to 0.
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of the contract.
function owner() public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_OWNER_SLOT)
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
// Compute the handover slot.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result := sload(keccak256(0x0c, 0x20))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by the owner.
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}{
"remappings": [
"solady/=lib/solady/src/",
"forge-std/=lib/forge-std/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "prague",
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_minter","type":"address"},{"internalType":"address","name":"_uriManager","type":"address"},{"internalType":"address","name":"_signer","type":"address"},{"internalType":"address","name":"_royaltyRecipient","type":"address"},{"internalType":"uint96","name":"_royaltyBips","type":"uint96"},{"internalType":"address","name":"_transferValidatorAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountBalanceOverflow","type":"error"},{"inputs":[],"name":"AlreadyClaimed","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ArrayLengthsMismatch","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NotOwnerNorApproved","type":"error"},{"inputs":[],"name":"RoyaltyOverflow","type":"error"},{"inputs":[],"name":"RoyaltyReceiverIsZeroAddress","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"TokenDoesNotExist","type":"error"},{"inputs":[],"name":"TransferToNonERC1155ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"isApproved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"subscriptionIdHash","type":"bytes32"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"roles","type":"uint256"}],"name":"RolesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldValidator","type":"address"},{"indexed":false,"internalType":"address","name":"newValidator","type":"address"}],"name":"TransferValidatorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"inputs":[],"name":"CLAIM_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINTING_ROLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"URI_MANAGEMENT_ROLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"owners","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"balances","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"batchAirdrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"batchBurn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"batchMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"subscriptionIdHash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"subscriptionIdHash","type":"bytes32"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"claimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTransferValidationFunction","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getTransferValidator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"grantRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAllRoles","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAnyRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"subscriptionIdHash","type":"bytes32"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"hasClaimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"structHash","type":"bytes32"}],"name":"hashTypedData","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"renounceRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"revokeRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"rolesOf","outputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"isApproved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint96","name":"feeNumerator","type":"uint96"}],"name":"setDefaultRoyalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"tokenURI","type":"string"}],"name":"setTokenURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"transferValidator","type":"address"}],"name":"setTransferValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"signer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURIs","outputs":[{"internalType":"string","name":"tokenURI","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"supply","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]Contract Creation Code
610120604052348015610010575f5ffd5b50604051612d8d380380612d8d83398101604081905261002f916102a0565b306080524660a05260608061007f604080518082018252601081526f4d6574614d61736b204176617461727360801b602080830191909152825180840190935260018352603160f81b9083015291565b815160209283012081519183019190912060c082905260e0819052604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8152938401929092529082015246606082015230608082015260a0902061010052506100ec90508761019a565b6100f78660026101d5565b6101028560016101d5565b5f80546001600160a01b0319166001600160a01b03861617905561012683836101e5565b6001600160a01b0381161561018e57600180546001600160a01b0319166001600160a01b038316908117909155604080515f815260208101929092527fcc5dc080ff977b3c3a211fa63ab74f90f658f5ba9d3236e92c8f59570f442aac910160405180910390a15b5050505050505061032f565b6001600160a01b0316638b78c6d819819055805f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b6101e18282600161022e565b5050565b6001600160601b0316612710808211156102065763350a88b35f526004601cfd5b8260601b8061021c5763b4457eaa5f526004601cfd5b90911768aa4ec00224afccfdb7555050565b638b78c6d8600c52825f526020600c2080548381178361024f575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f5fa3505050505050565b80516001600160a01b038116811461029b575f5ffd5b919050565b5f5f5f5f5f5f5f60e0888a0312156102b6575f5ffd5b6102bf88610285565b96506102cd60208901610285565b95506102db60408901610285565b94506102e960608901610285565b93506102f760808901610285565b60a08901519093506001600160601b0381168114610313575f5ffd5b915061032160c08901610285565b905092959891949750929550565b60805160a05160c05160e05161010051612a2161036c5f395f611bb201525f611c6c01525f611c4601525f611bf601525f611bd30152612a215ff3fe608060405260043610610274575f3560e01c8063514e62fc1161014a578063a22cb465116100be578063f04e283e11610078578063f04e283e14610834578063f242432a14610847578063f2fde38b14610866578063f5298aca14610879578063f6eb127a14610898578063fee81cf4146108b7575f5ffd5b8063a22cb46514610761578063a9fc664e14610780578063b816d0871461079f578063bd85b039146107be578063cff085ae146107e9578063e985e9c5146107fd575f5ffd5b80636c8b703f1161010f5780636c8b703f146106ad578063715018a6146106cc5780638127bcf2146106d457806384b0196e146106f35780638da5cb5b1461071a57806395d89b4114610732575f5ffd5b8063514e62fc146105ff57806354d1f13d146106345780636575f6aa1461063c5780636b0509b11461065b5780636c19e7831461068e575f5ffd5b80631cd64df4116101ec578063317484fe116101a6578063317484fe1461051b57806338d44a1b146105545780633c2455831461056857806340c10f19146105a15780634a4ee7b1146105c05780634e1273f4146105d3575f5ffd5b80631cd64df414610432578063238ac9331461046757806325692962146104855780632a55205a1461048d5780632de94807146104cb5780632eb2c2d6146104fc575f5ffd5b80630ca834801161023d5780630ca83480146103895780630d705df6146103a85780630e89341c146103ce578063162094c4146103ed578063183a4f6e1461040c5780631c10893f1461041f575f5ffd5b8062fdd58e1461027857806301ffc9a7146102c357806304634d8d146102f257806306fdde0314610313578063098144d414610358575b5f5ffd5b348015610283575f5ffd5b506102b06102923660046121c3565b679a31110384e0b0c96020526014919091525f908152604090205490565b6040519081526020015b60405180910390f35b3480156102ce575f5ffd5b506102e26102dd3660046121eb565b6108e8565b60405190151581526020016102ba565b3480156102fd575f5ffd5b5061031161030c366004612219565b61094e565b005b34801561031e575f5ffd5b506040805180820190915260148152734d6574614d61736b202b204f746865727369646560601b60208201525b6040516102ba919061228c565b348015610363575f5ffd5b506001546001600160a01b03165b6040516001600160a01b0390911681526020016102ba565b348015610394575f5ffd5b506103116103a3366004612361565b610964565b3480156103b3575f5ffd5b5060408051631854b24160e01b81525f6020820152016102ba565b3480156103d9575f5ffd5b5061034b6103e83660046123d3565b610a08565b3480156103f8575f5ffd5b506103116104073660046123ea565b610aa7565b61031161041a3660046123d3565b610b07565b61031161042d3660046121c3565b610b14565b34801561043d575f5ffd5b506102e261044c3660046121c3565b638b78c6d8600c9081525f9290925260209091205481161490565b348015610472575f5ffd5b505f54610371906001600160a01b031681565b610311610b26565b348015610498575f5ffd5b506104ac6104a7366004612486565b610b72565b604080516001600160a01b0390931683526020830191909152016102ba565b3480156104d6575f5ffd5b506102b06104e53660046124a6565b638b78c6d8600c9081525f91909152602090205490565b348015610507575f5ffd5b50610311610516366004612543565b610bc5565b348015610526575f5ffd5b506102e2610535366004612486565b600460209081525f928352604080842090915290825290205460ff1681565b34801561055f575f5ffd5b506102b0600281565b348015610573575f5ffd5b506102e2610582366004612486565b5f91825260046020908152604080842092845291905290205460ff1690565b3480156105ac575f5ffd5b506103116105bb3660046121c3565b610e88565b6103116105ce3660046121c3565b610edc565b3480156105de575f5ffd5b506105f26105ed3660046125ff565b610eee565b6040516102ba91906126a3565b34801561060a575f5ffd5b506102e26106193660046121c3565b638b78c6d8600c9081525f9290925260209091205416151590565b610311610f5b565b348015610647575f5ffd5b506102b06106563660046123d3565b610f94565b348015610666575f5ffd5b506102b07f921c01b2b37f8037e35da681d2e3f2cf2bdbd04ce87fa03672dcb3092ea9b12f81565b348015610699575f5ffd5b506103116106a83660046124a6565b610f9e565b3480156106b8575f5ffd5b5061034b6106c73660046123d3565b610fc7565b61031161105e565b3480156106df575f5ffd5b506103116106ee3660046126b5565b611071565b3480156106fe575f5ffd5b506107076111f0565b6040516102ba97969594939291906126f7565b348015610725575f5ffd5b50638b78c6d81954610371565b34801561073d575f5ffd5b50604080518082019091526007815266119bde13985d5d60ca1b602082015261034b565b34801561076c575f5ffd5b5061031161077b366004612766565b611253565b34801561078b575f5ffd5b5061031161079a3660046124a6565b6112a6565b3480156107aa575f5ffd5b506103116107b93660046125ff565b61130f565b3480156107c9575f5ffd5b506102b06107d83660046123d3565b60036020525f908152604090205481565b3480156107f4575f5ffd5b506102b0600181565b348015610808575f5ffd5b506102e2610817366004612794565b679a31110384e0b0c96020526014919091525f526034600c205490565b6103116108423660046124a6565b611407565b348015610852575f5ffd5b506103116108613660046127c5565b611441565b6103116108743660046124a6565b611610565b348015610884575f5ffd5b50610311610893366004612837565b611636565b3480156108a3575f5ffd5b506103116108b2366004612361565b61168f565b3480156108c2575f5ffd5b506102b06108d13660046124a6565b63389a75e1600c9081525f91909152602090205490565b5f61090f826301ffc9a760e09190911c90811463d9b67a26821417630e89341c9091141790565b8061092d5750632a55205a60e083901c9081146301ffc9a791909114175b8061094857506001600160e01b03198216632b435fdb60e21b145b92915050565b6109566116e8565b6109608282611702565b5050565b600261096f81611750565b5f5b83518110156109e75761099c84828151811061098f5761098f612867565b6020026020010151611781565b6109df578381815181106109b2576109b2612867565b602002602001015160405163c927e5bf60e01b81526004016109d691815260200190565b60405180910390fd5b600101610971565b50610a0284848460405180602001604052805f8152506117a6565b50505050565b5f818152600260205260409020805460609190610a249061287b565b80601f0160208091040260200160405190810160405280929190818152602001828054610a509061287b565b8015610a9b5780601f10610a7257610100808354040283529160200191610a9b565b820191905f5260205f20905b815481529060010190602001808311610a7e57829003601f168201915b50505050509050919050565b6001610ab281611750565b5f838152600260205260409020610ac983826128f7565b50827f6bb7ff708619ba0610cba295a58592e0451dee2622938c8755667688daf3529b83604051610afa919061228c565b60405180910390a2505050565b610b1133826118be565b50565b610b1c6116e8565b61096082826118c9565b5f6202a3006001600160401b03164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f5fa250565b5f82815268aa4ec00224afccfdb76020526040812054606081901c91906127109083610ba5576020515490508060601c93505b606084901b18845f19829004811182023d3d3e9396930204935090915050565b610c6588888888808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050604080516020808c0282810182019093528b82529093508b92508a9182918501908490808284375f9201919091525050604080516020601f8b0181900481028201810190925289815292508991508890819084018382808284375f920191909152506118d592505050565b828514610c7957633b800a465f526004601cfd5b8760601b679a31110384e0b0c9178760601b679a31110384e0b0c917816020528160601c99508060601c985088610cb75763ea553b345f526004601cfd5b893314610cd857335f526034600c2054610cd857634b6e7f185f526004601cfd5b8660051b5b8015610d45576020810390508087013583602052818a01355f5260405f20805480831115610d125763f4d678b85f526004601cfd5b8290039055602083905260405f20805480830181811015610d3a576301336cea5f526004601cfd5b90915550610cdd9050565b505050604051604081528560051b866040830152808860608401378060600160208301526060818301018781528187602083013750888a337f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb60808586010186a45050610daf5f90565b15610dc457610dc48888888888888888611add565b863b15610e7e57865f5260405163bc197c81815233602082015288604082015260a060608201528560051b8660c0830152808860e08401378060c001608083015260e08183010187815281876020830137818260e0010160a084015260208282010190508381528385602083013750808101830161010401905060208282601c604051015f5f515af1610e5f573d15610e5f573d5f833e3d82fd5b50805163bc197c8160e01b14610e7c57639c05499b5f526004601cfd5b505b5050505050505050565b6002610e9381611750565b610e9c82611781565b610ebc5760405163c927e5bf60e01b8152600481018390526024016109d6565b610ed78383600160405180602001604052805f815250611ae2565b505050565b610ee46116e8565b61096082826118be565b6060838214610f0457633b800a465f526004601cfd5b6040519050818152602081018260051b8181016040525b8015610f5157602081039050808701358060601b679a31110384e0b0c91760205250808501355f5260405f205481830152610f1b565b5050949350505050565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f5fa2565b5f61094882611bb0565b610fa66116e8565b5f80546001600160a01b0319166001600160a01b0392909216919091179055565b60026020525f908152604090208054610fdf9061287b565b80601f016020809104026020016040519081016040528092919081815260200182805461100b9061287b565b80156110565780601f1061102d57610100808354040283529160200191611056565b820191905f5260205f20905b81548152906001019060200180831161103957829003601f168201915b505050505081565b6110666116e8565b61106f5f611cc6565b565b61107a84611781565b61109a5760405163c927e5bf60e01b8152600481018590526024016109d6565b5f83815260046020908152604080832087845290915290205460ff16156110d457604051630c8d9eab60e31b815260040160405180910390fd5b604080517f921c01b2b37f8037e35da681d2e3f2cf2bdbd04ce87fa03672dcb3092ea9b12f6020820152339181019190915260608101849052608081018590525f9060a0016040516020818303038152906040528051906020012090505f61113b82611bb0565b5f54909150611155906001600160a01b0316828686611d03565b61117257604051638baa579f60e01b815260040160405180910390fd5b5f8581526004602090815260408083208984528252808320805460ff1916600190811790915581519283019091529181526111b1913391899190611ae2565b604051858152869033907fd95107f4584744c6c893a04c43058aadd1ce8aac8ca5d64140eaf277de6c1d579060200160405180910390a3505050505050565b600f60f81b6060805f808083611241604080518082018252601081526f4d6574614d61736b204176617461727360801b602080830191909152825180840190935260018352603160f81b9083015291565b97989097965046955030945091925090565b8015159050679a31110384e0b0c960205233601452815f52806034600c2055805f528160601b60601c337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160205fa35050565b6112ae6116e8565b600180546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527fcc5dc080ff977b3c3a211fa63ab74f90f658f5ba9d3236e92c8f59570f442aac910160405180910390a15050565b600261131a81611750565b83821461133a57604051631dc0052360e11b815260040160405180910390fd5b5f5b848110156113ff5761136584848381811061135957611359612867565b90506020020135611781565b61139e5783838281811061137b5761137b612867565b9050602002013560405163c927e5bf60e01b81526004016109d691815260200190565b6113f78686838181106113b3576113b3612867565b90506020020160208101906113c891906124a6565b8585848181106113da576113da612867565b90506020020135600160405180602001604052805f815250611ae2565b60010161133c565b505050505050565b61140f6116e8565b63389a75e1600c52805f526020600c20805442111561143557636f5e88185f526004601cfd5b5f9055610b1181611cc6565b6114b38686611463876040805180820190915260018152602081019190915290565b60408051808201909152600181526020810188905286868080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506118d592505050565b8560601b679a31110384e0b0c9178560601b679a31110384e0b0c917816020528160601c97508060601c9650866114f15763ea553b345f526004601cfd5b87331461151257335f526034600c205461151257634b6e7f185f526004601cfd5b855f5260405f2091508154808611156115325763f4d678b85f526004601cfd5b8581038355508060205260405f20915081548581018181101561155c576301336cea5f526004601cfd5b909255505060208390528486337fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6260405fa4843b156113ff5760405163f23a6e61815233602082015286604082015284606082015283608082015260a0808201528160c0820152818360e08301376020818360c401601c84015f8a5af16115eb573d156115eb573d5f823e3d81fd5b805163f23a6e6160e01b1461160757639c05499b5f526004601cfd5b50505050505050565b6116186116e8565b8060601b61162d57637448fbae5f526004601cfd5b610b1181611cc6565b336001600160a01b038416148015906116665750679a31110384e0b0c96020526014839052335f526034600c2054155b156116845760405163096dcfe360e31b815260040160405180910390fd5b610ed7838383611dd7565b336001600160a01b038416148015906116bf5750679a31110384e0b0c96020526014839052335f526034600c2054155b156116dd5760405163096dcfe360e31b815260040160405180910390fd5b610ed7838383611de3565b638b78c6d81954331461106f576382b429005f526004601cfd5b6bffffffffffffffffffffffff16612710808211156117285763350a88b35f526004601cfd5b8260601b8061173e5763b4457eaa5f526004601cfd5b90911768aa4ec00224afccfdb7555050565b638b78c6d8600c52335f52806020600c205416610b1157638b78c6d819543314610b11576382b429005f526004601cfd5b5f818152600260205260408120805482919061179c9061287b565b9050119050919050565b6117b35f858585856118d5565b81518351146117c957633b800a465f526004601cfd5b8360601b806117df5763ea553b345f526004601cfd5b80679a31110384e0b0c917602052835160051b5b801561182f5780840151818601515f5260405f20805482810181811015611821576301336cea5f526004601cfd5b9091555050601f19016117f3565b5060405160408152845160051b602001604082018181838960045afa503d60400160208401523d81019050855160051b60200191508181838860045afa50823d8201039150508260601c5f337f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8486a45050506118a95f90565b50833b15610a0257610a025f85858585611def565b61096082825f611ea8565b61096082826001611ea8565b6001600160a01b038516158015906118f557506001600160a01b03841615155b156119e6576001546001600160a01b031680156119e0575f5b84518110156119de57816001600160a01b0316631854b24133898989868151811061193b5761193b612867565b602002602001015189878151811061195557611955612867565b60209081029190910101516040516001600160e01b031960e088901b1681526001600160a01b03958616600482015293851660248501529390911660448301526064820152608481019190915260a4015f604051808303815f87803b1580156119bc575f5ffd5b505af11580156119ce573d5f5f3e3d5ffd5b50506001909201915061190e9050565b505b50611ad6565b6001600160a01b038516611a5e575f5b83518110156119e057828181518110611a1157611a11612867565b602002602001015160035f868481518110611a2e57611a2e612867565b602002602001015181526020019081526020015f205f828254611a5191906129c5565b90915550506001016119f6565b6001600160a01b038416611ad6575f5b83518110156113ff57828181518110611a8957611a89612867565b602002602001015160035f868481518110611aa657611aa6612867565b602002602001015181526020019081526020015f205f828254611ac991906129d8565b9091555050600101611a6e565b5050505050565b610e7e565b611b1f5f85611b04866040805180820190915260018152602081019190915290565b604080518082019091526001815260208101879052856118d5565b8360601b80611b355763ea553b345f526004601cfd5b679a31110384e0b0c960205284601452835f5260405f20805484810181811015611b66576301336cea5f526004601cfd5b808355505050826020528060601c5f337fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6260405fa450833b15610a0257610a025f85858585611eff565b7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000030147f0000000000000000000000000000000000000000000000000000000000000000461416611ca35750604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81527f000000000000000000000000000000000000000000000000000000000000000060208201527f00000000000000000000000000000000000000000000000000000000000000009181019190915246606082015230608082015260a090205b6719010000000000005f5280601a5281603a52604260182090505f603a52919050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a355565b5f6001600160a01b03851615611dcf57604051853b611d93578260408114611d335760418114611d5a5750611dcd565b60208581013560ff81901c601b0190915285356040526001600160ff1b0316606052611d6b565b60408501355f1a6020526040856040375b50845f526020600160805f60015afa5180871860601b3d119250505f60605280604052611dcd565b631626ba7e60e01b80825285600483015260248201604081528460448401528486606485013760208160648701858b5afa90519091141691505b505b949350505050565b610ed75f848484611f89565b610ed75f84848461206a565b60405163bc197c8181523360208201528560601b60601c604082015260a06060820152835160051b60200160c082018181838860045afa503d60a0018060808501523d82019150855160051b60200192508282848860045afa503d0160a0840152835160200191503d018181818660045afa50601c83013d82010391505060208282601c85015f8a5af1611e8b573d15611e8b573d5f833e3d82fd5b50805163bc197c8160e01b146113ff57639c05499b5f526004601cfd5b638b78c6d8600c52825f526020600c20805483811783611ec9575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f5fa3505050505050565b60405163f23a6e6181523360208201528560601b60601c604082015283606082015282608082015260a08082015281518060c08301528015611f4b578060e08301826020860160045afa505b6020828260c401601c85015f8a5af1611f6c573d15611f6c573d5f833e3d82fd5b50805163f23a6e6160e01b146113ff57639c05499b5f526004601cfd5b611fd4835f611fab856040805180820190915260018152602081019190915290565b60408051808201909152600181526020810186905260405180602001604052805f8152506118d5565b8260601b80679a31110384e0b0c917602052808560601b148560601b151761201057845f526034600c205461201057634b6e7f185f526004601cfd5b825f5260405f2080548084111561202e5763f4d678b85f526004601cfd5b83810382555050816020525f8160601c337fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6260405fa450610a02565b612085835f848460405180602001604052805f8152506118d5565b805182511461209b57633b800a465f526004601cfd5b8260601b80679a31110384e0b0c9176020528460601b8181148115176120d557855f526034600c20546120d557634b6e7f185f526004601cfd5b50825160051b5b80156121165780830151818501515f5260405f208054808311156121075763f4d678b85f526004601cfd5b919091039055601f19016120dc565b5060405160408152835160051b602001604082018181838860045afa503d60400160208401523d81019050845160051b60200191508181838760045afa50823d8201039150505f8360601c337f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8486a45050506121905f90565b15610a025760408051602081019091525f9052610a02565b80356001600160a01b03811681146121be575f5ffd5b919050565b5f5f604083850312156121d4575f5ffd5b6121dd836121a8565b946020939093013593505050565b5f602082840312156121fb575f5ffd5b81356001600160e01b031981168114612212575f5ffd5b9392505050565b5f5f6040838503121561222a575f5ffd5b612233836121a8565b915060208301356bffffffffffffffffffffffff81168114612253575f5ffd5b809150509250929050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f612212602083018461225e565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b03811182821017156122da576122da61229e565b604052919050565b5f82601f8301126122f1575f5ffd5b81356001600160401b0381111561230a5761230a61229e565b8060051b61231a602082016122b2565b91825260208185018101929081019086841115612335575f5ffd5b6020860192505b8383101561235757823582526020928301929091019061233c565b9695505050505050565b5f5f5f60608486031215612373575f5ffd5b61237c846121a8565b925060208401356001600160401b03811115612396575f5ffd5b6123a2868287016122e2565b92505060408401356001600160401b038111156123bd575f5ffd5b6123c9868287016122e2565b9150509250925092565b5f602082840312156123e3575f5ffd5b5035919050565b5f5f604083850312156123fb575f5ffd5b8235915060208301356001600160401b03811115612417575f5ffd5b8301601f81018513612427575f5ffd5b80356001600160401b038111156124405761244061229e565b612453601f8201601f19166020016122b2565b818152866020838501011115612467575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f5f60408385031215612497575f5ffd5b50508035926020909101359150565b5f602082840312156124b6575f5ffd5b612212826121a8565b5f5f83601f8401126124cf575f5ffd5b5081356001600160401b038111156124e5575f5ffd5b6020830191508360208260051b85010111156124ff575f5ffd5b9250929050565b5f5f83601f840112612516575f5ffd5b5081356001600160401b0381111561252c575f5ffd5b6020830191508360208285010111156124ff575f5ffd5b5f5f5f5f5f5f5f5f60a0898b03121561255a575f5ffd5b612563896121a8565b975061257160208a016121a8565b965060408901356001600160401b0381111561258b575f5ffd5b6125978b828c016124bf565b90975095505060608901356001600160401b038111156125b5575f5ffd5b6125c18b828c016124bf565b90955093505060808901356001600160401b038111156125df575f5ffd5b6125eb8b828c01612506565b999c989b5096995094979396929594505050565b5f5f5f5f60408587031215612612575f5ffd5b84356001600160401b03811115612627575f5ffd5b612633878288016124bf565b90955093505060208501356001600160401b03811115612651575f5ffd5b61265d878288016124bf565b95989497509550505050565b5f8151808452602084019350602083015f5b8281101561269957815186526020958601959091019060010161267b565b5093949350505050565b602081525f6122126020830184612669565b5f5f5f5f606085870312156126c8575f5ffd5b843593506020850135925060408501356001600160401b038111156126eb575f5ffd5b61265d87828801612506565b60ff60f81b8816815260e060208201525f61271560e083018961225e565b8281036040840152612727818961225e565b606084018890526001600160a01b038716608085015260a0840186905283810360c085015290506127588185612669565b9a9950505050505050505050565b5f5f60408385031215612777575f5ffd5b612780836121a8565b915060208301358015158114612253575f5ffd5b5f5f604083850312156127a5575f5ffd5b6127ae836121a8565b91506127bc602084016121a8565b90509250929050565b5f5f5f5f5f5f60a087890312156127da575f5ffd5b6127e3876121a8565b95506127f1602088016121a8565b9450604087013593506060870135925060808701356001600160401b03811115612819575f5ffd5b61282589828a01612506565b979a9699509497509295939492505050565b5f5f5f60608486031215612849575f5ffd5b612852846121a8565b95602085013595506040909401359392505050565b634e487b7160e01b5f52603260045260245ffd5b600181811c9082168061288f57607f821691505b6020821081036128ad57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115610ed757805f5260205f20601f840160051c810160208510156128d85750805b601f840160051c820191505b81811015611ad6575f81556001016128e4565b81516001600160401b038111156129105761291061229e565b6129248161291e845461287b565b846128b3565b6020601f821160018114612956575f831561293f5750848201515b5f19600385901b1c1916600184901b178455611ad6565b5f84815260208120601f198516915b828110156129855787850151825560209485019460019092019101612965565b50848210156129a257868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610948576109486129b1565b81810381811115610948576109486129b156fea2646970667358221220134e6aeaffd9555b68ae84e554aa86c90b45ec1b2a5649ede1daa2c449ad617764736f6c634300081c003300000000000000000000000029aeb8eb6ca5c1e355cd0b28b5f4824f3de6725c00000000000000000000000029aeb8eb6ca5c1e355cd0b28b5f4824f3de6725c00000000000000000000000029aeb8eb6ca5c1e355cd0b28b5f4824f3de6725c000000000000000000000000bacc168e7f3631b5c8a754431fafaf8b3c50b055000000000000000000000000548f6afb313b0031179f695398b70146a254f5f200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405260043610610274575f3560e01c8063514e62fc1161014a578063a22cb465116100be578063f04e283e11610078578063f04e283e14610834578063f242432a14610847578063f2fde38b14610866578063f5298aca14610879578063f6eb127a14610898578063fee81cf4146108b7575f5ffd5b8063a22cb46514610761578063a9fc664e14610780578063b816d0871461079f578063bd85b039146107be578063cff085ae146107e9578063e985e9c5146107fd575f5ffd5b80636c8b703f1161010f5780636c8b703f146106ad578063715018a6146106cc5780638127bcf2146106d457806384b0196e146106f35780638da5cb5b1461071a57806395d89b4114610732575f5ffd5b8063514e62fc146105ff57806354d1f13d146106345780636575f6aa1461063c5780636b0509b11461065b5780636c19e7831461068e575f5ffd5b80631cd64df4116101ec578063317484fe116101a6578063317484fe1461051b57806338d44a1b146105545780633c2455831461056857806340c10f19146105a15780634a4ee7b1146105c05780634e1273f4146105d3575f5ffd5b80631cd64df414610432578063238ac9331461046757806325692962146104855780632a55205a1461048d5780632de94807146104cb5780632eb2c2d6146104fc575f5ffd5b80630ca834801161023d5780630ca83480146103895780630d705df6146103a85780630e89341c146103ce578063162094c4146103ed578063183a4f6e1461040c5780631c10893f1461041f575f5ffd5b8062fdd58e1461027857806301ffc9a7146102c357806304634d8d146102f257806306fdde0314610313578063098144d414610358575b5f5ffd5b348015610283575f5ffd5b506102b06102923660046121c3565b679a31110384e0b0c96020526014919091525f908152604090205490565b6040519081526020015b60405180910390f35b3480156102ce575f5ffd5b506102e26102dd3660046121eb565b6108e8565b60405190151581526020016102ba565b3480156102fd575f5ffd5b5061031161030c366004612219565b61094e565b005b34801561031e575f5ffd5b506040805180820190915260148152734d6574614d61736b202b204f746865727369646560601b60208201525b6040516102ba919061228c565b348015610363575f5ffd5b506001546001600160a01b03165b6040516001600160a01b0390911681526020016102ba565b348015610394575f5ffd5b506103116103a3366004612361565b610964565b3480156103b3575f5ffd5b5060408051631854b24160e01b81525f6020820152016102ba565b3480156103d9575f5ffd5b5061034b6103e83660046123d3565b610a08565b3480156103f8575f5ffd5b506103116104073660046123ea565b610aa7565b61031161041a3660046123d3565b610b07565b61031161042d3660046121c3565b610b14565b34801561043d575f5ffd5b506102e261044c3660046121c3565b638b78c6d8600c9081525f9290925260209091205481161490565b348015610472575f5ffd5b505f54610371906001600160a01b031681565b610311610b26565b348015610498575f5ffd5b506104ac6104a7366004612486565b610b72565b604080516001600160a01b0390931683526020830191909152016102ba565b3480156104d6575f5ffd5b506102b06104e53660046124a6565b638b78c6d8600c9081525f91909152602090205490565b348015610507575f5ffd5b50610311610516366004612543565b610bc5565b348015610526575f5ffd5b506102e2610535366004612486565b600460209081525f928352604080842090915290825290205460ff1681565b34801561055f575f5ffd5b506102b0600281565b348015610573575f5ffd5b506102e2610582366004612486565b5f91825260046020908152604080842092845291905290205460ff1690565b3480156105ac575f5ffd5b506103116105bb3660046121c3565b610e88565b6103116105ce3660046121c3565b610edc565b3480156105de575f5ffd5b506105f26105ed3660046125ff565b610eee565b6040516102ba91906126a3565b34801561060a575f5ffd5b506102e26106193660046121c3565b638b78c6d8600c9081525f9290925260209091205416151590565b610311610f5b565b348015610647575f5ffd5b506102b06106563660046123d3565b610f94565b348015610666575f5ffd5b506102b07f921c01b2b37f8037e35da681d2e3f2cf2bdbd04ce87fa03672dcb3092ea9b12f81565b348015610699575f5ffd5b506103116106a83660046124a6565b610f9e565b3480156106b8575f5ffd5b5061034b6106c73660046123d3565b610fc7565b61031161105e565b3480156106df575f5ffd5b506103116106ee3660046126b5565b611071565b3480156106fe575f5ffd5b506107076111f0565b6040516102ba97969594939291906126f7565b348015610725575f5ffd5b50638b78c6d81954610371565b34801561073d575f5ffd5b50604080518082019091526007815266119bde13985d5d60ca1b602082015261034b565b34801561076c575f5ffd5b5061031161077b366004612766565b611253565b34801561078b575f5ffd5b5061031161079a3660046124a6565b6112a6565b3480156107aa575f5ffd5b506103116107b93660046125ff565b61130f565b3480156107c9575f5ffd5b506102b06107d83660046123d3565b60036020525f908152604090205481565b3480156107f4575f5ffd5b506102b0600181565b348015610808575f5ffd5b506102e2610817366004612794565b679a31110384e0b0c96020526014919091525f526034600c205490565b6103116108423660046124a6565b611407565b348015610852575f5ffd5b506103116108613660046127c5565b611441565b6103116108743660046124a6565b611610565b348015610884575f5ffd5b50610311610893366004612837565b611636565b3480156108a3575f5ffd5b506103116108b2366004612361565b61168f565b3480156108c2575f5ffd5b506102b06108d13660046124a6565b63389a75e1600c9081525f91909152602090205490565b5f61090f826301ffc9a760e09190911c90811463d9b67a26821417630e89341c9091141790565b8061092d5750632a55205a60e083901c9081146301ffc9a791909114175b8061094857506001600160e01b03198216632b435fdb60e21b145b92915050565b6109566116e8565b6109608282611702565b5050565b600261096f81611750565b5f5b83518110156109e75761099c84828151811061098f5761098f612867565b6020026020010151611781565b6109df578381815181106109b2576109b2612867565b602002602001015160405163c927e5bf60e01b81526004016109d691815260200190565b60405180910390fd5b600101610971565b50610a0284848460405180602001604052805f8152506117a6565b50505050565b5f818152600260205260409020805460609190610a249061287b565b80601f0160208091040260200160405190810160405280929190818152602001828054610a509061287b565b8015610a9b5780601f10610a7257610100808354040283529160200191610a9b565b820191905f5260205f20905b815481529060010190602001808311610a7e57829003601f168201915b50505050509050919050565b6001610ab281611750565b5f838152600260205260409020610ac983826128f7565b50827f6bb7ff708619ba0610cba295a58592e0451dee2622938c8755667688daf3529b83604051610afa919061228c565b60405180910390a2505050565b610b1133826118be565b50565b610b1c6116e8565b61096082826118c9565b5f6202a3006001600160401b03164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f5fa250565b5f82815268aa4ec00224afccfdb76020526040812054606081901c91906127109083610ba5576020515490508060601c93505b606084901b18845f19829004811182023d3d3e9396930204935090915050565b610c6588888888808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050604080516020808c0282810182019093528b82529093508b92508a9182918501908490808284375f9201919091525050604080516020601f8b0181900481028201810190925289815292508991508890819084018382808284375f920191909152506118d592505050565b828514610c7957633b800a465f526004601cfd5b8760601b679a31110384e0b0c9178760601b679a31110384e0b0c917816020528160601c99508060601c985088610cb75763ea553b345f526004601cfd5b893314610cd857335f526034600c2054610cd857634b6e7f185f526004601cfd5b8660051b5b8015610d45576020810390508087013583602052818a01355f5260405f20805480831115610d125763f4d678b85f526004601cfd5b8290039055602083905260405f20805480830181811015610d3a576301336cea5f526004601cfd5b90915550610cdd9050565b505050604051604081528560051b866040830152808860608401378060600160208301526060818301018781528187602083013750888a337f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb60808586010186a45050610daf5f90565b15610dc457610dc48888888888888888611add565b863b15610e7e57865f5260405163bc197c81815233602082015288604082015260a060608201528560051b8660c0830152808860e08401378060c001608083015260e08183010187815281876020830137818260e0010160a084015260208282010190508381528385602083013750808101830161010401905060208282601c604051015f5f515af1610e5f573d15610e5f573d5f833e3d82fd5b50805163bc197c8160e01b14610e7c57639c05499b5f526004601cfd5b505b5050505050505050565b6002610e9381611750565b610e9c82611781565b610ebc5760405163c927e5bf60e01b8152600481018390526024016109d6565b610ed78383600160405180602001604052805f815250611ae2565b505050565b610ee46116e8565b61096082826118be565b6060838214610f0457633b800a465f526004601cfd5b6040519050818152602081018260051b8181016040525b8015610f5157602081039050808701358060601b679a31110384e0b0c91760205250808501355f5260405f205481830152610f1b565b5050949350505050565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f5fa2565b5f61094882611bb0565b610fa66116e8565b5f80546001600160a01b0319166001600160a01b0392909216919091179055565b60026020525f908152604090208054610fdf9061287b565b80601f016020809104026020016040519081016040528092919081815260200182805461100b9061287b565b80156110565780601f1061102d57610100808354040283529160200191611056565b820191905f5260205f20905b81548152906001019060200180831161103957829003601f168201915b505050505081565b6110666116e8565b61106f5f611cc6565b565b61107a84611781565b61109a5760405163c927e5bf60e01b8152600481018590526024016109d6565b5f83815260046020908152604080832087845290915290205460ff16156110d457604051630c8d9eab60e31b815260040160405180910390fd5b604080517f921c01b2b37f8037e35da681d2e3f2cf2bdbd04ce87fa03672dcb3092ea9b12f6020820152339181019190915260608101849052608081018590525f9060a0016040516020818303038152906040528051906020012090505f61113b82611bb0565b5f54909150611155906001600160a01b0316828686611d03565b61117257604051638baa579f60e01b815260040160405180910390fd5b5f8581526004602090815260408083208984528252808320805460ff1916600190811790915581519283019091529181526111b1913391899190611ae2565b604051858152869033907fd95107f4584744c6c893a04c43058aadd1ce8aac8ca5d64140eaf277de6c1d579060200160405180910390a3505050505050565b600f60f81b6060805f808083611241604080518082018252601081526f4d6574614d61736b204176617461727360801b602080830191909152825180840190935260018352603160f81b9083015291565b97989097965046955030945091925090565b8015159050679a31110384e0b0c960205233601452815f52806034600c2055805f528160601b60601c337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160205fa35050565b6112ae6116e8565b600180546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527fcc5dc080ff977b3c3a211fa63ab74f90f658f5ba9d3236e92c8f59570f442aac910160405180910390a15050565b600261131a81611750565b83821461133a57604051631dc0052360e11b815260040160405180910390fd5b5f5b848110156113ff5761136584848381811061135957611359612867565b90506020020135611781565b61139e5783838281811061137b5761137b612867565b9050602002013560405163c927e5bf60e01b81526004016109d691815260200190565b6113f78686838181106113b3576113b3612867565b90506020020160208101906113c891906124a6565b8585848181106113da576113da612867565b90506020020135600160405180602001604052805f815250611ae2565b60010161133c565b505050505050565b61140f6116e8565b63389a75e1600c52805f526020600c20805442111561143557636f5e88185f526004601cfd5b5f9055610b1181611cc6565b6114b38686611463876040805180820190915260018152602081019190915290565b60408051808201909152600181526020810188905286868080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506118d592505050565b8560601b679a31110384e0b0c9178560601b679a31110384e0b0c917816020528160601c97508060601c9650866114f15763ea553b345f526004601cfd5b87331461151257335f526034600c205461151257634b6e7f185f526004601cfd5b855f5260405f2091508154808611156115325763f4d678b85f526004601cfd5b8581038355508060205260405f20915081548581018181101561155c576301336cea5f526004601cfd5b909255505060208390528486337fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6260405fa4843b156113ff5760405163f23a6e61815233602082015286604082015284606082015283608082015260a0808201528160c0820152818360e08301376020818360c401601c84015f8a5af16115eb573d156115eb573d5f823e3d81fd5b805163f23a6e6160e01b1461160757639c05499b5f526004601cfd5b50505050505050565b6116186116e8565b8060601b61162d57637448fbae5f526004601cfd5b610b1181611cc6565b336001600160a01b038416148015906116665750679a31110384e0b0c96020526014839052335f526034600c2054155b156116845760405163096dcfe360e31b815260040160405180910390fd5b610ed7838383611dd7565b336001600160a01b038416148015906116bf5750679a31110384e0b0c96020526014839052335f526034600c2054155b156116dd5760405163096dcfe360e31b815260040160405180910390fd5b610ed7838383611de3565b638b78c6d81954331461106f576382b429005f526004601cfd5b6bffffffffffffffffffffffff16612710808211156117285763350a88b35f526004601cfd5b8260601b8061173e5763b4457eaa5f526004601cfd5b90911768aa4ec00224afccfdb7555050565b638b78c6d8600c52335f52806020600c205416610b1157638b78c6d819543314610b11576382b429005f526004601cfd5b5f818152600260205260408120805482919061179c9061287b565b9050119050919050565b6117b35f858585856118d5565b81518351146117c957633b800a465f526004601cfd5b8360601b806117df5763ea553b345f526004601cfd5b80679a31110384e0b0c917602052835160051b5b801561182f5780840151818601515f5260405f20805482810181811015611821576301336cea5f526004601cfd5b9091555050601f19016117f3565b5060405160408152845160051b602001604082018181838960045afa503d60400160208401523d81019050855160051b60200191508181838860045afa50823d8201039150508260601c5f337f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8486a45050506118a95f90565b50833b15610a0257610a025f85858585611def565b61096082825f611ea8565b61096082826001611ea8565b6001600160a01b038516158015906118f557506001600160a01b03841615155b156119e6576001546001600160a01b031680156119e0575f5b84518110156119de57816001600160a01b0316631854b24133898989868151811061193b5761193b612867565b602002602001015189878151811061195557611955612867565b60209081029190910101516040516001600160e01b031960e088901b1681526001600160a01b03958616600482015293851660248501529390911660448301526064820152608481019190915260a4015f604051808303815f87803b1580156119bc575f5ffd5b505af11580156119ce573d5f5f3e3d5ffd5b50506001909201915061190e9050565b505b50611ad6565b6001600160a01b038516611a5e575f5b83518110156119e057828181518110611a1157611a11612867565b602002602001015160035f868481518110611a2e57611a2e612867565b602002602001015181526020019081526020015f205f828254611a5191906129c5565b90915550506001016119f6565b6001600160a01b038416611ad6575f5b83518110156113ff57828181518110611a8957611a89612867565b602002602001015160035f868481518110611aa657611aa6612867565b602002602001015181526020019081526020015f205f828254611ac991906129d8565b9091555050600101611a6e565b5050505050565b610e7e565b611b1f5f85611b04866040805180820190915260018152602081019190915290565b604080518082019091526001815260208101879052856118d5565b8360601b80611b355763ea553b345f526004601cfd5b679a31110384e0b0c960205284601452835f5260405f20805484810181811015611b66576301336cea5f526004601cfd5b808355505050826020528060601c5f337fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6260405fa450833b15610a0257610a025f85858585611eff565b7fd59c50ee9b502bf9b71f7ccfed1c6135ac5357533a7c46f9638c8a61afc7946e7f000000000000000000000000f63cc2c400e76764c958a3fd83b12cfd2ffa17d530147f0000000000000000000000000000000000000000000000000000000000008173461416611ca35750604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81527ff2a592d3f2e21edb24f5b26762139e82f69cb44d800d66d2473f9b87d94072a560208201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc69181019190915246606082015230608082015260a090205b6719010000000000005f5280601a5281603a52604260182090505f603a52919050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a355565b5f6001600160a01b03851615611dcf57604051853b611d93578260408114611d335760418114611d5a5750611dcd565b60208581013560ff81901c601b0190915285356040526001600160ff1b0316606052611d6b565b60408501355f1a6020526040856040375b50845f526020600160805f60015afa5180871860601b3d119250505f60605280604052611dcd565b631626ba7e60e01b80825285600483015260248201604081528460448401528486606485013760208160648701858b5afa90519091141691505b505b949350505050565b610ed75f848484611f89565b610ed75f84848461206a565b60405163bc197c8181523360208201528560601b60601c604082015260a06060820152835160051b60200160c082018181838860045afa503d60a0018060808501523d82019150855160051b60200192508282848860045afa503d0160a0840152835160200191503d018181818660045afa50601c83013d82010391505060208282601c85015f8a5af1611e8b573d15611e8b573d5f833e3d82fd5b50805163bc197c8160e01b146113ff57639c05499b5f526004601cfd5b638b78c6d8600c52825f526020600c20805483811783611ec9575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f5fa3505050505050565b60405163f23a6e6181523360208201528560601b60601c604082015283606082015282608082015260a08082015281518060c08301528015611f4b578060e08301826020860160045afa505b6020828260c401601c85015f8a5af1611f6c573d15611f6c573d5f833e3d82fd5b50805163f23a6e6160e01b146113ff57639c05499b5f526004601cfd5b611fd4835f611fab856040805180820190915260018152602081019190915290565b60408051808201909152600181526020810186905260405180602001604052805f8152506118d5565b8260601b80679a31110384e0b0c917602052808560601b148560601b151761201057845f526034600c205461201057634b6e7f185f526004601cfd5b825f5260405f2080548084111561202e5763f4d678b85f526004601cfd5b83810382555050816020525f8160601c337fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6260405fa450610a02565b612085835f848460405180602001604052805f8152506118d5565b805182511461209b57633b800a465f526004601cfd5b8260601b80679a31110384e0b0c9176020528460601b8181148115176120d557855f526034600c20546120d557634b6e7f185f526004601cfd5b50825160051b5b80156121165780830151818501515f5260405f208054808311156121075763f4d678b85f526004601cfd5b919091039055601f19016120dc565b5060405160408152835160051b602001604082018181838860045afa503d60400160208401523d81019050845160051b60200191508181838760045afa50823d8201039150505f8360601c337f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8486a45050506121905f90565b15610a025760408051602081019091525f9052610a02565b80356001600160a01b03811681146121be575f5ffd5b919050565b5f5f604083850312156121d4575f5ffd5b6121dd836121a8565b946020939093013593505050565b5f602082840312156121fb575f5ffd5b81356001600160e01b031981168114612212575f5ffd5b9392505050565b5f5f6040838503121561222a575f5ffd5b612233836121a8565b915060208301356bffffffffffffffffffffffff81168114612253575f5ffd5b809150509250929050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f612212602083018461225e565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b03811182821017156122da576122da61229e565b604052919050565b5f82601f8301126122f1575f5ffd5b81356001600160401b0381111561230a5761230a61229e565b8060051b61231a602082016122b2565b91825260208185018101929081019086841115612335575f5ffd5b6020860192505b8383101561235757823582526020928301929091019061233c565b9695505050505050565b5f5f5f60608486031215612373575f5ffd5b61237c846121a8565b925060208401356001600160401b03811115612396575f5ffd5b6123a2868287016122e2565b92505060408401356001600160401b038111156123bd575f5ffd5b6123c9868287016122e2565b9150509250925092565b5f602082840312156123e3575f5ffd5b5035919050565b5f5f604083850312156123fb575f5ffd5b8235915060208301356001600160401b03811115612417575f5ffd5b8301601f81018513612427575f5ffd5b80356001600160401b038111156124405761244061229e565b612453601f8201601f19166020016122b2565b818152866020838501011115612467575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f5f60408385031215612497575f5ffd5b50508035926020909101359150565b5f602082840312156124b6575f5ffd5b612212826121a8565b5f5f83601f8401126124cf575f5ffd5b5081356001600160401b038111156124e5575f5ffd5b6020830191508360208260051b85010111156124ff575f5ffd5b9250929050565b5f5f83601f840112612516575f5ffd5b5081356001600160401b0381111561252c575f5ffd5b6020830191508360208285010111156124ff575f5ffd5b5f5f5f5f5f5f5f5f60a0898b03121561255a575f5ffd5b612563896121a8565b975061257160208a016121a8565b965060408901356001600160401b0381111561258b575f5ffd5b6125978b828c016124bf565b90975095505060608901356001600160401b038111156125b5575f5ffd5b6125c18b828c016124bf565b90955093505060808901356001600160401b038111156125df575f5ffd5b6125eb8b828c01612506565b999c989b5096995094979396929594505050565b5f5f5f5f60408587031215612612575f5ffd5b84356001600160401b03811115612627575f5ffd5b612633878288016124bf565b90955093505060208501356001600160401b03811115612651575f5ffd5b61265d878288016124bf565b95989497509550505050565b5f8151808452602084019350602083015f5b8281101561269957815186526020958601959091019060010161267b565b5093949350505050565b602081525f6122126020830184612669565b5f5f5f5f606085870312156126c8575f5ffd5b843593506020850135925060408501356001600160401b038111156126eb575f5ffd5b61265d87828801612506565b60ff60f81b8816815260e060208201525f61271560e083018961225e565b8281036040840152612727818961225e565b606084018890526001600160a01b038716608085015260a0840186905283810360c085015290506127588185612669565b9a9950505050505050505050565b5f5f60408385031215612777575f5ffd5b612780836121a8565b915060208301358015158114612253575f5ffd5b5f5f604083850312156127a5575f5ffd5b6127ae836121a8565b91506127bc602084016121a8565b90509250929050565b5f5f5f5f5f5f60a087890312156127da575f5ffd5b6127e3876121a8565b95506127f1602088016121a8565b9450604087013593506060870135925060808701356001600160401b03811115612819575f5ffd5b61282589828a01612506565b979a9699509497509295939492505050565b5f5f5f60608486031215612849575f5ffd5b612852846121a8565b95602085013595506040909401359392505050565b634e487b7160e01b5f52603260045260245ffd5b600181811c9082168061288f57607f821691505b6020821081036128ad57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115610ed757805f5260205f20601f840160051c810160208510156128d85750805b601f840160051c820191505b81811015611ad6575f81556001016128e4565b81516001600160401b038111156129105761291061229e565b6129248161291e845461287b565b846128b3565b6020601f821160018114612956575f831561293f5750848201515b5f19600385901b1c1916600184901b178455611ad6565b5f84815260208120601f198516915b828110156129855787850151825560209485019460019092019101612965565b50848210156129a257868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610948576109486129b1565b81810381811115610948576109486129b156fea2646970667358221220134e6aeaffd9555b68ae84e554aa86c90b45ec1b2a5649ede1daa2c449ad617764736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000029aeb8eb6ca5c1e355cd0b28b5f4824f3de6725c00000000000000000000000029aeb8eb6ca5c1e355cd0b28b5f4824f3de6725c00000000000000000000000029aeb8eb6ca5c1e355cd0b28b5f4824f3de6725c000000000000000000000000bacc168e7f3631b5c8a754431fafaf8b3c50b055000000000000000000000000548f6afb313b0031179f695398b70146a254f5f200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _owner (address): 0x29AEB8EB6Ca5c1E355CD0B28B5F4824f3DE6725C
Arg [1] : _minter (address): 0x29AEB8EB6Ca5c1E355CD0B28B5F4824f3DE6725C
Arg [2] : _uriManager (address): 0x29AEB8EB6Ca5c1E355CD0B28B5F4824f3DE6725C
Arg [3] : _signer (address): 0xBACC168e7f3631B5c8A754431FAfaF8B3c50b055
Arg [4] : _royaltyRecipient (address): 0x548F6AfB313b0031179F695398b70146A254f5f2
Arg [5] : _royaltyBips (uint96): 0
Arg [6] : _transferValidatorAddress (address): 0x0000000000000000000000000000000000000000
-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 00000000000000000000000029aeb8eb6ca5c1e355cd0b28b5f4824f3de6725c
Arg [1] : 00000000000000000000000029aeb8eb6ca5c1e355cd0b28b5f4824f3de6725c
Arg [2] : 00000000000000000000000029aeb8eb6ca5c1e355cd0b28b5f4824f3de6725c
Arg [3] : 000000000000000000000000bacc168e7f3631b5c8a754431fafaf8b3c50b055
Arg [4] : 000000000000000000000000548f6afb313b0031179f695398b70146a254f5f2
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.