Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 2 internal transactions
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x6002601e | 22416295 | 293 days ago | Contract Creation | 0 ETH | |||
| 0x61010060 | 22416295 | 293 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
RulesetWhitelist
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "./RulesetBase.sol";
import "../interfaces/IWhitelistExtension.sol";
import "@limitbreak/tm-core-lib/src/utils/token/IEOARegistry.sol";
/**
* @title RulesetWhitelist
* @author Limit Break, Inc.
* @notice A ruleset contract that blocks transfers by default, unless exceptions are made through various options and
* whitelisting rules.
*/
contract RulesetWhitelist is IRuleset, RulesetBase {
using EnumerableSet for EnumerableSet.AddressSet;
using EnumerableSet for EnumerableSet.Bytes32Set;
struct Options {
bool enableAccountFreezingMode;
bool disableAuthorizationMode;
bool customListSupplementsDefaultList;
bool blockSmartWalletReceivers;
bool blockUnverifiedEOAReceivers;
bool blockAllOTC;
bool allowOTCFor7702Delegates;
bool allowOTCForSmartWallets;
}
/// @dev The address of the EOA Registry to use to validate an account is a verified EOA.
address private immutable _eoaRegistry;
constructor(address eoaRegistry_) RulesetBase() {
if (eoaRegistry_ == address(0)) {
revert CreatorTokenTransferValidator__InvalidConstructorArgs();
}
_eoaRegistry = eoaRegistry_;
}
/**
* @notice Validates a transfer of a Creator Token.
*
* @notice Global Options Used:
* - Disable Authorization Mode: Toggles authorization mode logic on/off (default: enabled)
* - Enable Account Freezing Mode: Toggles account freezing logic on/off (default: disabled)
* - Custom List Supplements Default List Mode: When toggled on, whitelist includes both custom list
* values and default managed list values (default: off)
*
* @notice Ruleset-Specific Options Used:
* - Block Smart Wallet Receivers: When enabled, blocks transfers to receivers with
* code length > 0, unless `to` is whitelisted (default: disabled)
* - Block Unverified EOA Receivers: When enabled, blocks transfers to receivers that have not verified
* an ECDSA signature on the EOA registry, unless `to` is whitelisted
* (default: disabled)
* - Block All OTC: When enabled, blocks all OTC transfers, unless `caller` or `from` are whitelisted
* (default: disabled)
* [OTC transfer is defined as any owner-initiated transfer where caller == from]
* - Allow OTC For 7702 Delegates: Unused when Block All OTC is enabled. When enabled, allows OTC transfers
* from EOAs that currently have EIP-7702 delegates attached. When disabled,
* blocks OTC transfers from EOAs that currently have EIP-7702 delegates
* attached, unless the attached delegate is whitelisted.
* (default: disabled)
* - Allow OTC For Smart Wallets: Unused when Block All OTC is enabled. When enabled, allows OTC transfers
* from smart wallets. When disabled, blocks OTC transfers from smart wallets,
* unless the smart wallet is whitelisted. (default: disabled)
*
* @notice Validation Flow:
* 1. If account freezing mode is enabled, check if the sender or recipient is frozen.
* If frozen, block transfer and return.
* 2. If authorization mode is enabled, check if an authorizer has pre-authorized transfer of
* collection/token id/amount by operator (msg.sender). If authorized, allow transfer and return.
* 3. If block smart wallet receivers is enabled, check if the receiver has code length > 0. If so,
* check if the receiver is whitelisted. If not whitelisted, block transfer and return.
* If whitelisted, continue.
* 4. If block unverified EOA receivers is enabled, check if the receiver has verified an ECDSA signature
* on the EOA registry. If not verified, check if the receiver is whitelisted. If not whitelisted,
* block transfer and return. If whitelisted, continue.
* 5. If transfer is OTC (caller == from):
* 5a. If block all OTC is enabled, check if `caller` or `from` are whitelisted.
* If not whitelisted, block transfer and return. If whitelisted, allow transfer and return.
* 5b. If block all OTC is disabled, and code length of `from` address equals 0, allow transfer and return.
* 5c. If block all OTC is disabled, and code length of `from` address is greater than 0:
* 5ci. If `from` is an EOA with delegation attached: when allow OTC for 7702 delegates is enabled,
* allow transfer an return. Otherwise, when allow OTC for 7702 delegates is disabled, check
* if delegate is whitelisted. If not whitelisted, block transfer and return. If whitelisted,
* allow transfer and return.
* 5cii. Otherwise, `from` is a smart wallet. If allow OTC for smart wallets is enabled, allow
* transfer and return. Otherwise, check if `from` is whitelisted. If not whitelisted, block
* transfer and return. If whitelisted, allow transfer and return.
* 6. If transfer is not OTC (caller != from), check if `caller` is whitelisted. If not whitelisted,
* block transfer and return. If whitelisted, allow transfer and return.
*
* @notice Whitelisting Notes:
* - When performing a whitelist check, when custom list supplements default list mode is enabled, the
* default whitelist is checked first. If the operator is not whitelisted in the default list, the
* custom list specified in the collection security policy is checked.
* - When performing whitelist checks, accounts are first checked by address, then by codehash, and finally
* evaluated against a set of 0 or more whitelist extensions.
*/
function validateTransfer(
uint256 authorizerCheckType,
address collection,
address caller,
address from,
address to,
uint256 tokenId,
uint256 amount) external view returns (bytes4) {
CollectionSecurityPolicy storage collectionSecurityPolicy =
validatorStorage().collectionSecurityPolicies[collection];
Options memory opts =
_getOptions(collectionSecurityPolicy.globalOptions, collectionSecurityPolicy.rulesetOptions);
if (opts.enableAccountFreezingMode) {
AccountList storage frozenAccountList = validatorStorage().frozenAccounts[collection];
if (frozenAccountList.nonEnumerableAccounts[from]) {
return CreatorTokenTransferValidator__SenderAccountIsFrozen.selector;
}
if (frozenAccountList.nonEnumerableAccounts[to]) {
return CreatorTokenTransferValidator__ReceiverAccountIsFrozen.selector;
}
}
if (!opts.disableAuthorizationMode) {
if (_isAuthorized(authorizerCheckType, collection, caller, tokenId, amount)) {
return SELECTOR_NO_ERROR;
}
}
return _doWhitelistChecks(
opts,
collectionSecurityPolicy.listId,
collection,
to,
caller,
from);
}
function _doWhitelistChecks(
Options memory opts,
uint48 listId,
address collection,
address to,
address caller,
address from
) internal view returns (bytes4) {
(
mapping (address => bool) storage accountWhitelist,
mapping (bytes32 => bool) storage codehashWhitelist,
EnumerableSet.AddressSet storage whitelistExtensions
) = _getWhitelistStoragePointers(listId);
if (opts.blockSmartWalletReceivers) {
if (_getCodeLengthAsm(to) > 0) {
if (!_isWhitelisted(opts.customListSupplementsDefaultList, accountWhitelist, codehashWhitelist, whitelistExtensions, collection, to)) {
return CreatorTokenTransferValidator__ReceiverMustNotHaveDeployedCode.selector;
}
}
}
if (opts.blockUnverifiedEOAReceivers) {
if (!_isVerifiedEOA(to)) {
if (!_isWhitelisted(opts.customListSupplementsDefaultList, accountWhitelist, codehashWhitelist, whitelistExtensions, collection, to)) {
return CreatorTokenTransferValidator__ReceiverProofOfEOASignatureUnverified.selector;
}
}
}
if (caller == from) {
if (!opts.blockAllOTC) {
if (_getCodeLengthAsm(from) > 0) {
(bool isDelegate, address delegate) = _check7702(from);
if (opts.allowOTCFor7702Delegates) {
// If allowing OTC for 7702 delegates, check if the owner is delegated or not
if (!isDelegate) {
// This is a non-delegated smart wallet account, if OTC is not allowed for smart wallets check against whitelist
if (!opts.allowOTCForSmartWallets) {
if (!_isWhitelisted(opts.customListSupplementsDefaultList, accountWhitelist, codehashWhitelist, whitelistExtensions, collection, from)) {
return CreatorTokenTransferValidator__OTCNotAllowedForSmartWallets.selector;
}
}
}
} else {
// If not allowing OTC for 7702 delegates, check if the owner is delegated or not
if (isDelegate) {
// This is a delegated EOA account, block unless delegate is whitelisted
if (!_is7702DelegateWhitelisted(opts.customListSupplementsDefaultList, listId, collection, delegate)) {
return CreatorTokenTransferValidator__OTCNotAllowedFor7702Delegates.selector;
}
} else {
// This is a non-delegated smart wallet account, if OTC is not allowed for smart wallets check against whitelist
if (!opts.allowOTCForSmartWallets) {
if (!_isWhitelisted(opts.customListSupplementsDefaultList, accountWhitelist, codehashWhitelist, whitelistExtensions, collection, from)) {
return CreatorTokenTransferValidator__OTCNotAllowedForSmartWallets.selector;
}
}
}
}
}
return SELECTOR_NO_ERROR;
}
}
if (!_isWhitelisted(opts.customListSupplementsDefaultList, accountWhitelist, codehashWhitelist, whitelistExtensions, collection, caller, from)) {
return CreatorTokenTransferValidator__CallerOrFromMustBeWhitelisted.selector;
}
return SELECTOR_NO_ERROR;
}
function _isWhitelisted(
bool customListSupplementsDefaultList,
mapping (address => bool) storage accountWhitelist,
mapping (bytes32 => bool) storage codehashWhitelist,
EnumerableSet.AddressSet storage whitelistExtensions,
address collection,
address account
) internal view returns (bool isWhitelisted) {
if (customListSupplementsDefaultList) {
(
mapping (address => bool) storage defaultAccountWhitelist,
mapping (bytes32 => bool) storage defaultCodehashWhitelist,
EnumerableSet.AddressSet storage defaultWhitelistExtensions
) = _getWhitelistStoragePointers(DEFAULT_LIST_ID);
if (_checkWhitelist(
defaultAccountWhitelist,
defaultCodehashWhitelist,
defaultWhitelistExtensions,
collection,
account)) {
return true;
}
}
return _checkWhitelist(accountWhitelist, codehashWhitelist, whitelistExtensions, collection, account);
}
function _isWhitelisted(
bool customListSupplementsDefaultList,
mapping (address => bool) storage accountWhitelist,
mapping (bytes32 => bool) storage codehashWhitelist,
EnumerableSet.AddressSet storage whitelistExtensions,
address collection,
address account1,
address account2
) internal view returns (bool isWhitelisted) {
if (customListSupplementsDefaultList) {
(
mapping (address => bool) storage defaultAccountWhitelist,
mapping (bytes32 => bool) storage defaultCodehashWhitelist,
EnumerableSet.AddressSet storage defaultWhitelistExtensions
) = _getWhitelistStoragePointers(DEFAULT_LIST_ID);
if (_checkWhitelist(
defaultAccountWhitelist,
defaultCodehashWhitelist,
defaultWhitelistExtensions,
collection,
account1,
account2)) {
return true;
}
}
return _checkWhitelist(accountWhitelist, codehashWhitelist, whitelistExtensions, collection, account1, account2);
}
function _is7702DelegateWhitelisted(
bool customListSupplementsDefaultList,
uint48 listId,
address collection,
address account
) internal view returns (bool isWhitelisted) {
if (customListSupplementsDefaultList) {
(
mapping (address => bool) storage defaultAccountWhitelist,
mapping (bytes32 => bool) storage defaultCodehashWhitelist,
EnumerableSet.AddressSet storage defaultWhitelistExtensions
) = _get7702DelegateWhitelistStoragePointers(DEFAULT_LIST_ID);
if (_checkWhitelist(
defaultAccountWhitelist,
defaultCodehashWhitelist,
defaultWhitelistExtensions,
collection,
account)) {
return true;
}
}
(
mapping (address => bool) storage accountWhitelist,
mapping (bytes32 => bool) storage codehashWhitelist,
EnumerableSet.AddressSet storage whitelistExtensions
) = _get7702DelegateWhitelistStoragePointers(listId);
return _checkWhitelist(accountWhitelist, codehashWhitelist, whitelistExtensions, collection, account);
}
function _checkWhitelist(
mapping (address => bool) storage accountWhitelist,
mapping (bytes32 => bool) storage codehashWhitelist,
EnumerableSet.AddressSet storage whitelistExtensions,
address collection,
address account
) internal view returns (bool isWhitelisted) {
isWhitelisted =
accountWhitelist[account] ||
codehashWhitelist[_getCodeHashAsm(account)];
if (!isWhitelisted) {
uint256 lengthOfExtensionsList = whitelistExtensions.length();
for (uint256 i = 0; i < lengthOfExtensionsList; ++i) {
IWhitelistExtension whitelistExtension = IWhitelistExtension(whitelistExtensions.at(i));
if (whitelistExtension.isWhitelisted(collection, account)) {
isWhitelisted = true;
break;
}
}
}
}
function _checkWhitelist(
mapping (address => bool) storage accountWhitelist,
mapping (bytes32 => bool) storage codehashWhitelist,
EnumerableSet.AddressSet storage whitelistExtensions,
address collection,
address account1,
address account2
) internal view returns (bool isWhitelisted) {
isWhitelisted =
accountWhitelist[account1] ||
accountWhitelist[account2] ||
codehashWhitelist[_getCodeHashAsm(account1)] ||
codehashWhitelist[_getCodeHashAsm(account2)];
if (!isWhitelisted) {
uint256 lengthOfExtensionsList = whitelistExtensions.length();
for (uint256 i = 0; i < lengthOfExtensionsList; ++i) {
IWhitelistExtension whitelistExtension = IWhitelistExtension(whitelistExtensions.at(i));
if (whitelistExtension.isWhitelisted(collection, account1) ||
whitelistExtension.isWhitelisted(collection, account2)) {
isWhitelisted = true;
break;
}
}
}
}
/// @notice Returns true if the specified account has verified a signature on the registry, false otherwise.
function _isVerifiedEOA(address account) internal view returns (bool) {
return IEOARegistry(_eoaRegistry).isVerifiedEOA(account);
}
function _getOptions(uint8 globalOptions, uint16 whitelistOptions) internal pure returns (Options memory options) {
options.enableAccountFreezingMode =
_isFlagSet(globalOptions, FLAG_GLOBAL_ENABLE_ACCOUNT_FREEZING_MODE);
options.disableAuthorizationMode =
_isFlagSet(globalOptions, FLAG_GLOBAL_DISABLE_AUTHORIZATION_MODE);
options.customListSupplementsDefaultList =
_isFlagSet(globalOptions, FLAG_GLOBAL_CUSTOM_LIST_SUPPLEMENTS_DEFAULT_LIST);
options.blockSmartWalletReceivers =
_isFlagSet(whitelistOptions, FLAG_RULESET_WHITELIST_BLOCK_SMART_WALLET_RECEIVERS);
options.blockUnverifiedEOAReceivers =
_isFlagSet(whitelistOptions, FLAG_RULESET_WHITELIST_BLOCK_UNVERIFIED_EOA_RECEIVERS);
options.blockAllOTC =
_isFlagSet(whitelistOptions, FLAG_RULESET_WHITELIST_BLOCK_ALL_OTC);
options.allowOTCFor7702Delegates =
_isFlagSet(whitelistOptions, FLAG_RULESET_WHITELIST_ALLOW_OTC_FOR_7702_DELEGATES);
options.allowOTCForSmartWallets =
_isFlagSet(whitelistOptions, FLAG_RULESET_WHITELIST_ALLOW_OTC_FOR_SMART_WALLETS);
}
function _getWhitelistStoragePointers(uint48 listId)
internal
view
returns (
mapping (address => bool) storage accountWhitelist,
mapping (bytes32 => bool) storage codehashWhitelist,
EnumerableSet.AddressSet storage whitelistExtensions
) {
List storage whitelist = validatorStorage().lists[LIST_TYPE_WHITELIST][listId];
accountWhitelist = whitelist.nonEnumerableAccounts;
codehashWhitelist = whitelist.nonEnumerableCodehashes;
whitelistExtensions =
validatorStorage().lists[EXPANSION_LIST_TYPE_WHITELIST_EXTENSION_CONTRACTS][listId].enumerableAccounts;
}
function _get7702DelegateWhitelistStoragePointers(uint48 listId)
internal
view
returns (
mapping (address => bool) storage accountWhitelist,
mapping (bytes32 => bool) storage codehashWhitelist,
EnumerableSet.AddressSet storage whitelistExtensions
) {
List storage whitelist =
validatorStorage().lists[EXPANSION_LIST_TYPE_7702_DELEGATE_WHITELIST][listId];
accountWhitelist = whitelist.nonEnumerableAccounts;
codehashWhitelist = whitelist.nonEnumerableCodehashes;
whitelistExtensions =
validatorStorage().lists[EXPANSION_LIST_TYPE_7702_DELEGATE_WHITELIST_EXTENSION_CONTRACTS][listId].enumerableAccounts;
}
}pragma solidity ^0.8.4;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}pragma solidity ^0.8.24;
library StorageTstorish {
// keccak256(abi.encode(uint256(keccak256("storage.Tstorish")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant DATA_STORAGE_SLOT =
0xdacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b00;
struct Data {
// Indicates if TSTORE support has been activated during or post-deployment.
bool tstoreSupport;
}
function data() internal pure returns (Data storage ptr) {
bytes32 slot = DATA_STORAGE_SLOT;
assembly {
ptr.slot := slot
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "./StorageTstorish.sol";
/**
* @title Tloadish
* @notice Based on https://github.com/ProjectOpenSea/tstorish/commit/a81ed74453ed7b9fe7e96a9906bc4def19b73e33
*/
abstract contract Tloadish {
/*
* ------------------------------------------------------------------------+
* Opcode | Mnemonic | Stack | Memory |
* ------------------------------------------------------------------------|
* 60 0x02 | PUSH1 0x02 | 0x02 | |
* 60 0x1e | PUSH1 0x1e | 0x1e 0x02 | |
* 61 0x3d5c | PUSH2 0x3d5c | 0x3d5c 0x1e 0x02 | |
* 3d | RETURNDATASIZE | 0 0x3d5c 0x1e 0x02 | |
* |
* :: store deployed bytecode in memory: (3d) RETURNDATASIZE (5c) TLOAD :: |
* 52 | MSTORE | 0x1e 0x02 | [0..0x20): 0x3d5c |
* f3 | RETURN | | [0..0x20): 0x3d5c |
* ------------------------------------------------------------------------+
*/
uint256 constant _TLOAD_TEST_PAYLOAD = 0x6002_601e_613d5c_3d_52_f3;
uint256 constant _TLOAD_TEST_PAYLOAD_LENGTH = 0x0a;
uint256 constant _TLOAD_TEST_PAYLOAD_OFFSET = 0x16;
// Declare an immutable variable to store the tload test contract address.
address private immutable _tloadTestContract;
// Declare an immutable variable to store the initial TLOAD support status.
bool internal immutable _tloadInitialSupport;
// Declare an immutable function type variable for the _getTloadish function
// based on chain support for tload at time of deployment.
function(uint256) view returns (uint256) internal immutable _getTstorish;
// Declare a few custom revert error types.
error TloadTestContractDeploymentFailed();
/**
* @dev Determine TLOAD availability during deployment. This involves
* attempting to deploy a contract that utilizes TLOAD as part of the
* contract construction bytecode, and configuring initial support for
* using TLOAD in place of SLOAD based on the result.
*/
constructor() {
// Deploy the contract testing TLOAD support and store the address.
address tloadTestContract = _prepareTloadTest();
// Ensure the deployment was successful.
if (tloadTestContract == address(0)) {
revert TloadTestContractDeploymentFailed();
}
// Determine if TLOAD is supported.
_tloadInitialSupport = StorageTstorish.data().tstoreSupport = _testTload(tloadTestContract);
if (_tloadInitialSupport) {
// If TLOAD is supported, set functions to their versions that use
// tload directly without support checks.
_getTstorish = _getTstore;
} else {
// If TLOAD is not supported, set functions to their versions that
// fallback to sload until tstoreSupport is true.
_getTstorish = _getTstorishWithSloadFallback;
}
// Set the address of the deployed TLOAD test contract as an immutable.
_tloadTestContract = tloadTestContract;
}
/**
* @dev Private function to read a TSTORISH value. Assigned to _getTstorish
* internal function variable at construction if chain has tload support.
*
* @param storageSlot The slot to read the TSTORISH value from.
*
* @return value The TSTORISH value at the given storage slot.
*/
function _getTstore(
uint256 storageSlot
) internal view returns (uint256 value) {
assembly {
value := tload(storageSlot)
}
}
/**
* @dev Private function to read a TSTORISH value with sload fallback.
* Assigned to _getTstorish internal function variable at construction
* if chain does not have tload support.
*
* @param storageSlot The slot to read the TSTORISH value from.
*
* @return value The TSTORISH value at the given storage slot.
*/
function _getTstorishWithSloadFallback(
uint256 storageSlot
) internal view returns (uint256 value) {
if (StorageTstorish.data().tstoreSupport) {
assembly {
value := tload(storageSlot)
}
} else {
assembly {
value := sload(storageSlot)
}
}
}
/**
* @dev Private function to deploy a test contract that utilizes TLOAD as
* part of its fallback logic.
*/
function _prepareTloadTest() private returns (address contractAddress) {
// Utilize assembly to deploy a contract testing TLOAD support.
assembly {
// Write the contract deployment code payload to scratch space.
mstore(0, _TLOAD_TEST_PAYLOAD)
// Deploy the contract.
contractAddress := create(
0,
_TLOAD_TEST_PAYLOAD_OFFSET,
_TLOAD_TEST_PAYLOAD_LENGTH
)
}
}
/**
* @dev Private view function to determine if TLOAD is supported by
* the current EVM implementation by attempting to call the test
* contract, which utilizes TLOAD as part of its fallback logic.
*/
function _testTload(
address tloadTestContract
) private view returns (bool ok) {
// Call the test contract, which will perform a TLOAD test. If the call
// does not revert, then TLOAD is supported. Do not forward all
// available gas, as all forwarded gas will be consumed on revert.
(ok, ) = tloadTestContract.staticcall{ gas: gasleft() / 10 }("");
}
}pragma solidity ^0.8.4;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}pragma solidity ^0.8.4;
import "../introspection/IERC165.sol";
interface IEOARegistry is IERC165 {
function isVerifiedEOA(address account) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
/**************************************************************/
/* LIST TYPES */
/**************************************************************/
uint8 constant LIST_TYPE_BLACKLIST = 0;
uint8 constant LIST_TYPE_WHITELIST = 1;
uint8 constant LIST_TYPE_AUTHORIZERS = 2;
uint8 constant EXPANSION_LIST_TYPE_WHITELIST_EXTENSION_CONTRACTS = 3;
uint8 constant EXPANSION_LIST_TYPE_7702_DELEGATE_WHITELIST = 4;
uint8 constant EXPANSION_LIST_TYPE_7702_DELEGATE_WHITELIST_EXTENSION_CONTRACTS = 5;
/**************************************************************/
/* RULESET IDS */
/**************************************************************/
uint8 constant RULESET_ID_DEFAULT = 0;
uint8 constant RULESET_ID_VANILLA = 1;
uint8 constant RULESET_ID_SOULBOUND = 2;
uint8 constant RULESET_ID_BLACKLIST = 3;
uint8 constant RULESET_ID_WHITELIST = 4;
uint8 constant RULESET_ID_FIXED_OR_CUSTOM = 255;
/**************************************************************/
/* AUTHORIZER CHECK TYPES */
/**************************************************************/
uint256 constant AUTHORIZER_CHECK_TYPE_TOKEN = 1;
uint256 constant AUTHORIZER_CHECK_TYPE_COLLECTION = 2;
uint256 constant AUTHORIZER_CHECK_TYPE_TOKEN_AND_AMOUNT = 3;
/**************************************************************/
/* MISCELLANEOUS */
/**************************************************************/
bytes4 constant LEGACY_TRANSFER_VALIDATOR_INTERFACE_ID = bytes4(0x00000000);
bytes4 constant SELECTOR_NO_ERROR = bytes4(0x00000000);
bytes32 constant BYTES32_ZERO = 0x0000000000000000000000000000000000000000000000000000000000000000;
bytes32 constant DEFAULT_ACCESS_CONTROL_ADMIN_ROLE = 0x0000000000000000000000000000000000000000000000000000000000000000;
address constant WILDCARD_OPERATOR_ADDRESS = address(0x01);
uint48 constant DEFAULT_LIST_ID = 0;
uint16 constant DEFAULT_TOKEN_TYPE = 0;
/**************************************************************/
/* FLAGS - GLOBAL */
/**************************************************************/
// Flags are used to efficiently store and retrieve boolean values in a single uint8.
// Each flag is a power of 2, so they can be combined using bitwise OR (|) and checked using bitwise AND (&).
// For example, to set the first and third flags, you would use: flags = FLAG1 | FLAG3;
// To check if the first flag is set, you would use: if (flags & FLAG1 != 0) { ... }
uint8 constant FLAG_GLOBAL_DISABLE_AUTHORIZATION_MODE = 1 << 0;
uint8 constant FLAG_GLOBAL_AUTHORIZERS_CANNOT_SET_WILDCARD_OPERATORS = 1 << 1;
uint8 constant FLAG_GLOBAL_ENABLE_ACCOUNT_FREEZING_MODE = 1 << 2;
uint8 constant FLAG_GLOBAL_CUSTOM_LIST_SUPPLEMENTS_DEFAULT_LIST = 1 << 3;
/**************************************************************/
/* FLAGS - RULESET WHITELIST */
/**************************************************************/
uint16 constant FLAG_RULESET_WHITELIST_BLOCK_ALL_OTC = 1 << 0;
uint16 constant FLAG_RULESET_WHITELIST_ALLOW_OTC_FOR_7702_DELEGATES = 1 << 1;
uint16 constant FLAG_RULESET_WHITELIST_ALLOW_OTC_FOR_SMART_WALLETS = 1 << 2;
uint16 constant FLAG_RULESET_WHITELIST_BLOCK_SMART_WALLET_RECEIVERS = 1 << 3;
uint16 constant FLAG_RULESET_WHITELIST_BLOCK_UNVERIFIED_EOA_RECEIVERS = 1 << 4;// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "@limitbreak/tm-core-lib/src/utils/structs/EnumerableSet.sol";
/**
* @dev This struct contains the security policy settings for a collection.
*/
struct CollectionSecurityPolicy {
uint8 rulesetId;
uint48 listId;
address customRuleset;
uint8 globalOptions;
uint16 rulesetOptions;
uint16 tokenType;
}
/**
* @dev This struct is internally for the storage of account and codehash lists.
*/
struct List {
EnumerableSet.AddressSet enumerableAccounts;
EnumerableSet.Bytes32Set enumerableCodehashes;
mapping (address => bool) nonEnumerableAccounts;
mapping (bytes32 => bool) nonEnumerableCodehashes;
}
/**
* @dev This struct is internally for the storage of account lists.
*/
struct AccountList {
EnumerableSet.AddressSet enumerableAccounts;
mapping (address => bool) nonEnumerableAccounts;
}
/**
* @dev This struct contains a key and value pair for future expansion data words that are 32 bytes long.
*/
struct ExpansionWord {
bytes32 key;
bytes32 value;
}
/**
* @dev This struct contains a key and value pair for future expansion data bytes that are variable length.
*/
struct ExpansionDatum {
bytes32 key;
bytes value;
}
/**
* @dev This struct contains the storage layout for the validator contract, excluding Permit-C and Tstorish data.
*/
struct ValidatorStorage {
/// @notice Keeps track of the most recently created list id.
uint48 lastListId;
/// @dev Used as a collision guard.
mapping (address => address) transientOperator;
/// @notice Mapping of list ids to list owners
mapping (uint48 => address) listOwners;
/// @dev Mapping of collection addresses to their security policy settings
mapping (address => CollectionSecurityPolicy) collectionSecurityPolicies;
/// @dev Mapping of collections to accounts that are frozen for those collections
mapping (address => AccountList) frozenAccounts;
/// @dev Mapping of list ids to list data
mapping (uint8 => mapping (uint48 => List)) lists;
/// @dev Mapping of collection addressses to any future expansion data words settings that may be used in the future
mapping (address collection => mapping (bytes32 extension => bytes32 word)) collectionExpansionWords;
/// @dev Mapping of collection addressses to any future expansion data bytes settings that may be used in the future
mapping (address collection => mapping (bytes32 extension => bytes data)) collectionExpansionDatums;
/// @dev Mapping of addresses to a boolean indicating if they are registered trusted validator modules
mapping (address => bool) registeredRulesets;
/// @dev Mapping of security levels to their current implementation modules
mapping (uint8 => address) rulesetBindings;
}// SPDX-License-Identifier: MIT pragma solidity 0.8.24; /// @dev Thrown when admin attempts to bind a ruleset to the fixed/custom ruleset ID. error CreatorTokenTransferValidator__AdminCannotAssignRulesetToRulesetIdCustom(); /// @dev Thrown when validating transfers with amount if authorization by amount mode is active and the amount /// exceeds the pre-authorized amount. error CreatorTokenTransferValidator__AmountExceedsAuthorization(); /// @dev Thrown when attempting to set a authorized operator when authorization mode is disabled. error CreatorTokenTransferValidator__AuthorizationDisabledForCollection(); /// @dev Thrown when attempting to call a function that requires the caller to be the list owner. error CreatorTokenTransferValidator__CallerDoesNotOwnList(); /// @dev Thrown when authorizing a transfer for a collection using authorizers and the msg.sender is not in the /// authorizer list. error CreatorTokenTransferValidator__CallerMustBeAnAuthorizer(); /// @dev Thrown when validating a transfer for a collection using whitelists and the operator is not on the whitelist. error CreatorTokenTransferValidator__CallerMustBeWhitelisted(); /// @dev Thrown when attempting to call a function that requires owner or default admin role for a collection that the /// caller does not have. error CreatorTokenTransferValidator__CallerMustHaveElevatedPermissionsForSpecifiedNFT(); /// @dev Thrown when validating a transfer for a collection using whitelists and the operator or from account is not on the whitelist. error CreatorTokenTransferValidator__CallerOrFromMustBeWhitelisted(); /// @dev Thrown when attempting to renounce ownership of the default list id. error CreatorTokenTransferValidator__CannotRenounceOwnershipOfDefaultList(); /// @dev Thrown when setting the ruleset for a collection when a reserved ruleset id is used, but a custom ruleset /// is specified. error CreatorTokenTransferValidator__CannotSetCustomRulesetOnManagedRulesetId(); /// @dev Thrown when constructor args are not valid error CreatorTokenTransferValidator__InvalidConstructorArgs(); /// @dev Thrown when setting the transfer security level to an invalid value. error CreatorTokenTransferValidator__InvalidTransferSecurityLevel(); /// @dev Thrown when attempting to set a list id that does not exist. error CreatorTokenTransferValidator__ListDoesNotExist(); /// @dev Thrown when attempting to transfer the ownership of a list to the zero address. error CreatorTokenTransferValidator__ListOwnershipCannotBeTransferredToZeroAddress(); /// @dev Thrown when attempting to call the transfer validation logic externally, as staticcall guarantees are needed. error CreatorTokenTransferValidator__OnlyValidatorCanAccessThisFunction(); /// @dev Thrown when validating a transfer for a collection using blacklists and the operator is on the blacklist. error CreatorTokenTransferValidator__OperatorIsBlacklisted(); /// @dev Thrown when validating an OTC transfer with EIP-7702 Delegation when disabled by security settings. error CreatorTokenTransferValidator__OTCNotAllowedFor7702Delegates(); /// @dev Thrown when validating an OTC transfer from Smart Wallets when disabled by security settings. error CreatorTokenTransferValidator__OTCNotAllowedForSmartWallets(); /// @dev Thrown when a frozen account is the receiver of a transfer error CreatorTokenTransferValidator__ReceiverAccountIsFrozen(); /// @dev Thrown when validating a transfer for a collection that does not allow receiver to have code and the receiver /// has code. error CreatorTokenTransferValidator__ReceiverMustNotHaveDeployedCode(); /// @dev Thrown when validating a transfer for a collection that requires receivers be verified EOAs and the receiver /// is not verified. error CreatorTokenTransferValidator__ReceiverProofOfEOASignatureUnverified(); /// @dev Thrown when attempting to register a ruleset that is not a contract. error CreatorTokenTransferValidator__RulesetIsNotContract(); /// @dev Thrown when attempting to register a ruleset that is not pure. error CreatorTokenTransferValidator__RulesetIsNotPure(); /// @dev Thrown when admin attempts to bind a ruleset that is not registered, or a collection admin tries to /// set their fixed/custom ruleset to an unregistered ruleset. error CreatorTokenTransferValidator__RulesetIsNotRegistered(); /// @dev Thrown when a frozen account is the sender of a transfer error CreatorTokenTransferValidator__SenderAccountIsFrozen(); /// @dev Thrown when validating a transfer for a collection that is in soulbound token mode. error CreatorTokenTransferValidator__TokenIsSoulbound(); /// @dev Thrown when attempting to validate a permitted transfer where the permit type does not match the /// collection-defined token type. error CreatorTokenTransferValidator__TokenTypesDoNotMatch(); /// @dev Thrown when an authorizer attempts to set a wildcard authorized operator on collections that don't /// allow wildcards error CreatorTokenTransferValidator__WildcardOperatorsCannotBeAuthorizedForCollection();
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "./DataTypes.sol";
import "./Errors.sol";
/**
* @title ValidatorBase
* @author Limit Break, Inc.
* @notice Base contract for all Creator Token Validator, Module, and Ruleset contracts.
* Includes some helper functions and modifiers and easy access to validator diamond storage
* to allow modules and rulesets to access the storage of the validator contract.
*/
contract ValidatorBase {
/*************************************************************************/
/* MODIFIERS */
/*************************************************************************/
/**
* @dev This modifier restricts a function call to the owner of the list `id`.
* @dev Throws when the caller is not the list owner.
*/
modifier onlyListOwner(uint48 id) {
_requireCallerOwnsList(id);
_;
}
/*************************************************************************/
/* HELPERS */
/*************************************************************************/
/**
* @notice Requires the caller to be the owner of list `id`.
*
* @dev Throws when the caller is not the owner of the list.
*
* @param id The id of the list to check ownership of.
*/
function _requireCallerOwnsList(uint48 id) private view {
if (msg.sender != validatorStorage().listOwners[id]) {
revert CreatorTokenTransferValidator__CallerDoesNotOwnList();
}
}
/**
* @dev Internal function used to compute the transient storage slot for the authorized
* operator of a token in a collection.
*
* @param collection The collection address of the token being transferred.
* @param tokenId The id of the token being transferred.
*
* @return operatorSlot The storage slot location for the authorized operator value.
*/
function _getTransientOperatorSlot(
address collection,
uint256 tokenId
) internal pure returns (uint256 operatorSlot) {
assembly {
mstore(0x00, collection)
mstore(0x20, tokenId)
operatorSlot := shr(4, keccak256(0x00, 0x40))
}
}
/**
* @dev Internal function used to compute the transient storage slot for the authorized operator of a collection.
*
* @param collection The collection address of the token being transferred.
*
* @return operatorSlot The storage slot location for the authorized operator value.
*/
function _getTransientOperatorSlot(address collection) internal view returns (uint256 operatorSlot) {
mapping (address => address) storage _transientOperator = validatorStorage().transientOperator;
assembly {
mstore(0x00, collection)
mstore(0x20, _transientOperator.slot)
operatorSlot := keccak256(0x00, 0x40)
}
}
/**
* @dev Internal function used to efficiently retrieve the code length of `account`.
*
* @param account The address to get the deployed code length for.
*
* @return length The length of deployed code at the address.
*/
function _getCodeLengthAsm(address account) internal view returns (uint256 length) {
assembly { length := extcodesize(account) }
}
/**
* @dev Internal function used to efficiently retrieve the codehash of `account`.
*
* @param account The address to get the deployed codehash for.
*
* @return codehash The codehash of the deployed code at the address.
*/
function _getCodeHashAsm(address account) internal view returns (bytes32 codehash) {
assembly { codehash := extcodehash(account) }
}
/**
* @notice Returns true if the `flagValue` has the `flag` set, false otherwise.
*
* @dev This function uses the bitwise AND operator to check if the `flag` is set in `flagValue`.
*
* @param flagValue The value to check for the presence of the `flag`.
* @param flag The flag to check for in the `flagValue`.
*/
function _isFlagSet(uint256 flagValue, uint256 flag) internal pure returns (bool flagSet) {
flagSet = (flagValue & flag) != 0;
}
/*************************************************************************/
/* STORAGE */
/*************************************************************************/
/// @dev The base storage slot for Validator V5 contract storage items.
bytes32 constant DIAMOND_STORAGE_VALIDATOR =
0x0000000000000000000000000000000000000000000000000000000000721C05;
/**
* @dev Returns a storage object that follows the Diamond standard storage pattern for
* @dev contract storage across multiple module contracts.
*/
function validatorStorage() internal pure returns (ValidatorStorage storage diamondStorage) {
bytes32 slot = DIAMOND_STORAGE_VALIDATOR;
assembly {
diamondStorage.slot := slot
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
/**
* @title IRuleset
* @author Limit Break, Inc.
* @notice Interface for Creator Token Standards ruleset contracts.
*
* @dev Ruleset contracts are logic-gate contracts used to validate transfers of Creator Tokens
* (ERC20-C / ERC721-C / ERC1155-C). Rulesets are view-only, and may not contain any opcodes
* that could modify the state of the blockchain.
*/
interface IRuleset {
/**
* @notice Validates a transfer of a Creator Token.
*
* @param authorizerCheckType The type of authorizer check to perform.
* @param collection The address of the Creator Token contract.
* @param caller The address of the caller (msg.sender) of the transfer function.
* @param from The address of the sender of the token.
* @param to The address of the recipient of the token.
* @param tokenId The ID of the token.
* @param amount The amount of the token to transfer.
* @return 0x00000000 when the transfer is allowed, or a custom error selector to block a transfer.
*/
function validateTransfer(
uint256 authorizerCheckType,
address collection,
address caller,
address from,
address to,
uint256 tokenId,
uint256 amount) external view returns (bytes4);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
/**
* @title IRuleset
* @author Limit Break, Inc.
* @notice Interface for whitelist extension contracts for the Whitelist Ruleset.
*
* @dev The Whitelist Ruleset supports whitelisting of specific accounts and codehashes.
* A Whitelist Extension contract offers an additional mechanism to perform more sophisticated
* checks to see if an account can be allowed to perform a transfer. For example, imagine a collection
* that blocks OTC transfers from smart wallet accounts because a smart wallet could be used to faciliate
* a trade that violates the creator's rules on trading. A Whitelist Extension contract could be used to
* query a trusted smart wallet factory that is known to adhere to the creator's rules, and allow any wallet
* originating from that factory to perform OTC trades or to receive tokens when in a mode that blocks contracts
* from receiving tokens.
*/
interface IWhitelistExtension {
/**
* @notice Perform an advanced check on an account to see if it should be whitelisted.
*
* @param collection The address of the Creator Token contract.
* @param account The address of the account to check.
* @return true if the account is whitelisted, false otherwise.
*/
function isWhitelisted(
address collection,
address account) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "../Constants.sol";
import "../DataTypes.sol";
import "../Errors.sol";
import "../ValidatorBase.sol";
import "../interfaces/IRuleset.sol";
import "@limitbreak/tm-core-lib/src/utils/misc/Tloadish.sol";
/**
* @title RulesetBase
* @author Limit Break, Inc.
* @notice Base contract that includes internal helper functions and diamond storage access that are
* useful for implementations of Creator Token Standards ruleset contracts.
*/
abstract contract RulesetBase is ValidatorBase, Tloadish {
/**
* @dev Internal function to check if a caller is authorized to transfer a token/id/amount.
*
* @param authorizerCheckType The type of authorizer check to perform.
* @param collection The address of the Creator Token contract.
* @param caller The address of the caller (msg.sender) of the transfer function.
* @param tokenId The ID of the token.
* @param amount The amount of the token to transfer.
*
* @return isAuthorized True if the caller is authorized to transfer the token, false otherwise.
*/
function _isAuthorized(
uint256 authorizerCheckType,
address collection,
address caller,
uint256 tokenId,
uint256 amount
) internal view returns (bool isAuthorized) {
isAuthorized = _getAuthorizerCheckFunctionPointer(authorizerCheckType)(
collection, caller, tokenId, amount
);
}
/**
* @dev Internal function to get the function pointer for the authorizer check type.
*
* @param authorizerCheckType The type of authorizer check to perform.
*
* @return _callerAuthorizedCheck The function pointer to the authorizer check function.
*/
function _getAuthorizerCheckFunctionPointer(uint256 authorizerCheckType) internal pure returns (function(address,address,uint256, uint256) internal view returns(bool) _callerAuthorizedCheck) {
if (authorizerCheckType == AUTHORIZER_CHECK_TYPE_TOKEN) {
return _callerAuthorizedCheckToken;
} else if (authorizerCheckType == AUTHORIZER_CHECK_TYPE_COLLECTION) {
return _callerAuthorizedCheckCollection;
} else {
// The master validator handles the authorized amount check, so we won't do anything different here to use
// the authorized amount and the amount parameter.
// Current rulesets won't use this call, it is a placeholder for future ruleset use.
return _callerAuthorizedCheckCollection;
}
}
/**
* @dev Internal function to check if a caller is an authorized operator for the token being transferred.
*
* @param caller The caller of the token transfer.
* @param collection The collection address of the token being transferred.
* @param tokenId The id of the token being transferred.
*
* @return isAuthorized True if the caller is authorized to transfer the token, false otherwise.
*/
function _callerAuthorizedCheckToken(
address collection,
address caller,
uint256 tokenId,
uint256 /*amount*/
) internal view returns (bool isAuthorized) {
uint256 slotValue;
(isAuthorized, ) = _callerAuthorized(caller, _getTransientOperatorSlot(collection, tokenId));
if (isAuthorized) return true;
(isAuthorized, slotValue) = _callerAuthorized(caller, _getTransientOperatorSlot(collection));
isAuthorized = isAuthorized && slotValue >> 255 == 1;
}
/**
* @dev Internal function to check if a caller is an authorized operator for the collection being transferred.
*
* @param caller The caller of the token transfer.
* @param collection The collection address of the token being transferred.
*
* @return isAuthorized True if the caller is authorized to transfer the collection, false otherwise.
*/
function _callerAuthorizedCheckCollection(
address collection,
address caller,
uint256 /*tokenId*/,
uint256 /*amount*/
) internal view returns (bool isAuthorized) {
(isAuthorized, ) = _callerAuthorized(caller, _getTransientOperatorSlot(collection));
}
/**
* @dev Internal function to check if a caller is an authorized operator.
* @dev This overload of `_callerAuthorized` checks a specific storage slot for the caller address.
*
* @param caller The caller of the token transfer.
* @param slot The storage slot to check for the caller address.
*
* @return isAuthorized True if the caller is authorized to transfer the token, false otherwise.
* @return slotValue The transient storage value in `slot`, used to check for allow any token id flag if necessary.
*/
function _callerAuthorized(address caller, uint256 slot) internal view returns (bool isAuthorized, uint256 slotValue) {
slotValue = _getTstorish(slot);
address authorizedOperator = address(uint160(slotValue));
isAuthorized = authorizedOperator == WILDCARD_OPERATOR_ADDRESS || authorizedOperator == caller;
}
/**
* @dev Internal function to check if an address is an EOA in EIP-7702 delegation mode, and what address
* it is delegated to.
*
* @param _address The account to check for EIP-7702 delegation.
*
* @return isDelegate True if the account is an EOA with an EIP-7702 delegate attached, false otherwise.
* @return delegate The address of the EIP-7702 delegate code, if one is attached.
*/
function _check7702(address _address) internal view returns (bool isDelegate, address delegate) {
assembly ("memory-safe") {
mstore(0x00, 0x00)
extcodecopy(_address, 0x1D, 0x00, 0x17)
if eq(mload(0x00), 0xEF0100) {
isDelegate := 1
delegate := shr(0x60, mload(0x20))
}
}
}
}{
"remappings": [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"@limitbreak/tm-core-lib/=lib/tm-core-lib/",
"@limitbreak/permit-c/=lib/PermitC/src/",
"@openzeppelin/=lib/PermitC/lib/openzeppelin-contracts/",
"@rari-capital/solmate/=lib/PermitC/lib/solmate/",
"PermitC/=lib/PermitC/",
"erc4626-tests/=lib/PermitC/lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-gas-metering/=lib/PermitC/lib/forge-gas-metering/",
"openzeppelin-contracts/=lib/PermitC/lib/openzeppelin-contracts/",
"openzeppelin/=lib/PermitC/lib/openzeppelin-contracts/contracts/",
"solady/=lib/PermitC/lib/forge-gas-metering/lib/solady/",
"solmate/=lib/PermitC/lib/solmate/src/",
"tm-core-lib/=lib/tm-core-lib/src/"
],
"optimizer": {
"enabled": true,
"runs": 1000000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": false
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"eoaRegistry_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CreatorTokenTransferValidator__InvalidConstructorArgs","type":"error"},{"inputs":[],"name":"TloadTestContractDeploymentFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"authorizerCheckType","type":"uint256"},{"internalType":"address","name":"collection","type":"address"},{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"validateTransfer","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"}]Contract Creation Code
61010060405234801562000011575f80fd5b5060405162001164380380620011648339810160408190526200003491620001dc565b5f6200003f62000127565b90506001600160a01b0381166200006957604051632aea588760e01b815260040160405180910390fd5b620000748162000140565b7fdacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b00805460ff19169115801592831790915560a091909152620000ce576001600160401b0362000277620001a260201b171660c052620000e7565b6001600160401b036200027b620001a660201b171660c0525b6001600160a01b039081166080528116620001155760405163c56e5d7360e01b815260040160405180910390fd5b6001600160a01b031660e0526200022b565b5f696002601e613d5c3d52f35f52600a60165ff0905090565b5f816001600160a01b0316600a5a6200015a91906200020b565b6040515f8181818686fa925050503d805f811462000194576040519150601f19603f3d011682016040523d82523d5f602084013e62000199565b606091505b50909392505050565b5c90565b5f7fdacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b005460ff1615620001d757505c90565b505490565b5f60208284031215620001ed575f80fd5b81516001600160a01b038116811462000204575f80fd5b9392505050565b5f826200022657634e487b7160e01b5f52601260045260245ffd5b500490565b60805160a05160c05160e051610f0b620002595f395f6106c901525f610d3401525f50505f5050610f0b5ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c80630be200ea1461002d575b5f80fd5b61004061003b366004610e26565b610075565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200160405180910390f35b73ffffffffffffffffffffffffffffffffffffffff86165f90815262721c08602052604081208054829061014f9060ff7b010000000000000000000000000000000000000000000000000000008204169061ffff7c01000000000000000000000000000000000000000000000000000000009091041660408051610100810182526004848116151582526001808616151560208401526008958616151593830193909352938316151560608201526010831615156080820152908216151560a082015260028216151560c08201529116151560e082015290565b8051909150156102235773ffffffffffffffffffffffffffffffffffffffff8981165f90815262721c0960209081526040808320938b1683526002840190915290205460ff16156101c657507f2b635c8800000000000000000000000000000000000000000000000000000000925061026c915050565b73ffffffffffffffffffffffffffffffffffffffff87165f90815260028201602052604090205460ff161561022157507f2a5cb1c300000000000000000000000000000000000000000000000000000000925061026c915050565b505b8060200151610249576102398a8a8a88886102b4565b1561024957505f915061026c9050565b8154610267908290610100900465ffffffffffff168b898c8c6102d7565b925050505b979650505050505050565b5c90565b5f7fdacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b005460ff16156102ab57505c90565b5080545b919050565b5f6102cd858585856102c58b610594565b63ffffffff16565b9695505050505050565b65ffffffffffff85165f9081527fdddabf5c2ba4d65801ab32cdb63ad695571faeb901010108d1a350518718439e602090815260408083207f2841ea725888be53292047902c89cc990fdbb15dbc2e5b558a6a052c722a0ede90925282206060890151600483019260050191901561039257863b15610392576103628a604001518484848c8c6105c2565b61039257507faca58aa00000000000000000000000000000000000000000000000000000000092506102cd915050565b8960800151156103eb576103a587610682565b6103eb576103bb8a604001518484848c8c6105c2565b6103eb57507fce32f2aa0000000000000000000000000000000000000000000000000000000092506102cd915050565b8473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1603610542578960a0015161054257843b15610536575f806104398761073a565b915091508b60c001511561049e5781610499578b60e00151610499576104678c604001518686868e8c6105c2565b61049957507f42d0e0e40000000000000000000000000000000000000000000000000000000094506102cd9350505050565b610533565b81156104e6576104b48c604001518c8c84610762565b61049957507f369c2f4c0000000000000000000000000000000000000000000000000000000094506102cd9350505050565b8b60e00151610533576105018c604001518686868e8c6105c2565b61053357507f42d0e0e40000000000000000000000000000000000000000000000000000000094506102cd9350505050565b50505b505f92506102cd915050565b6105558a604001518484848c8b8b610898565b61058557507fe1f1d02e0000000000000000000000000000000000000000000000000000000092506102cd915050565b505f9998505050505050505050565b610df9600182036105a85750610966919050565b600282036105b957506109d3919050565b506109d3919050565b5f8615610675575f80527f2841ea725888be53292047902c89cc990fdbb15dbc2e5b558a6a052c722a0ede6020527f938cff0dbdc4b4032da5e0e3a4947fb40aba826911a8ad09a80c555fd5830d5b7f938cff0dbdc4b4032da5e0e3a4947fb40aba826911a8ad09a80c555fd5830d5c7fe6367c6c33ddbb3b5b8a8c588253bb03f885a3f85345efae1611865a80acc52361066083838389896109f7565b1561067157600193505050506102cd565b5050505b61026c86868686866109f7565b6040517f89a9c85500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301525f917f0000000000000000000000000000000000000000000000000000000000000000909116906389a9c85590602401602060405180830381865afa158015610710573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107349190610e92565b92915050565b5f805f805260175f601d853c62ef01005f510361075d57505060205160019060601c5b915091565b5f8415610815575f80527fe91a6628aa83f010ff9afa7f160b18736f245c1821f24ffae38c046526028eb16020527f739a1fa67523b96335a4175253d835d43315604e3ab95d6deb68d9488a6242f17f739a1fa67523b96335a4175253d835d43315604e3ab95d6deb68d9488a6242f27fa731cdb379c53870826417699a62d8514fe9e04ae3343c38d8e6940d1ccb6bca61080083838389896109f7565b156108115760019350505050610890565b5050505b65ffffffffffff84165f9081527f5e58bed0817b31bdcd1d55e32930f39f8107eac1d4c0fd8dbef0c8c7e7389b6f602090815260408083207fe91a6628aa83f010ff9afa7f160b18736f245c1821f24ffae38c046526028eb190925290912060048201916005019061088a83838389896109f7565b93505050505b949350505050565b5f871561094c575f80527f2841ea725888be53292047902c89cc990fdbb15dbc2e5b558a6a052c722a0ede6020527f938cff0dbdc4b4032da5e0e3a4947fb40aba826911a8ad09a80c555fd5830d5b7f938cff0dbdc4b4032da5e0e3a4947fb40aba826911a8ad09a80c555fd5830d5c7fe6367c6c33ddbb3b5b8a8c588253bb03f885a3f85345efae1611865a80acc5236109378383838a8a8a610b1f565b15610948576001935050505061026c565b5050505b61095a878787878787610b1f565b98975050505050505050565b5f806109878561098288875f9182526020526040902060041c90565b610d2c565b509150811561099a576001915050610890565b6109b485610982885f90815262721c066020526040902090565b90925090508180156102cd575060ff81901c6001149695505050505050565b5f84815262721c06602052604081206109ed908590610d2c565b5095945050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526020869052604081205460ff1680610a395750813f5f9081526020869052604090205460ff165b905080610b16575f610a4a85610db8565b90505f5b81811015610b13575f610a618783610dc1565b6040517fb6b3527200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff888116600483015287811660248301529192509082169063b6b3527290604401602060405180830381865afa158015610ad7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610afb9190610e92565b15610b0a576001935050610b13565b50600101610a4e565b50505b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff82165f9081526020879052604081205460ff1680610b76575073ffffffffffffffffffffffffffffffffffffffff82165f9081526020889052604090205460ff165b80610b905750823f5f9081526020879052604090205460ff165b80610baa5750813f5f9081526020879052604090205460ff165b9050806102cd575f610bbb86610db8565b90505f5b81811015610d20575f610bd28883610dc1565b6040517fb6b3527200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff898116600483015288811660248301529192509082169063b6b3527290604401602060405180830381865afa158015610c48573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c6c9190610e92565b80610d0857506040517fb6b3527200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8881166004830152868116602483015282169063b6b3527290604401602060405180830381865afa158015610ce4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d089190610e92565b15610d17576001935050610d20565b50600101610bbf565b50509695505050505050565b5f80610d5b837f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b90508073ffffffffffffffffffffffffffffffffffffffff811660011480610dae57508473ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b9250509250929050565b5f610734825490565b5f610dcc8383610dd3565b9392505050565b5f825f018281548110610de857610de8610eb1565b905f5260205f200154905092915050565b610e01610ede565b565b803573ffffffffffffffffffffffffffffffffffffffff811681146102af575f80fd5b5f805f805f805f60e0888a031215610e3c575f80fd5b87359650610e4c60208901610e03565b9550610e5a60408901610e03565b9450610e6860608901610e03565b9350610e7660808901610e03565b925060a0880135915060c0880135905092959891949750929550565b5f60208284031215610ea2575f80fd5b81518015158114610dcc575f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52605160045260245ffd000000000000000000000000e0a0004dfa318fc38298ae81a666710eadceba5c
Deployed Bytecode
0x608060405234801561000f575f80fd5b5060043610610029575f3560e01c80630be200ea1461002d575b5f80fd5b61004061003b366004610e26565b610075565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200160405180910390f35b73ffffffffffffffffffffffffffffffffffffffff86165f90815262721c08602052604081208054829061014f9060ff7b010000000000000000000000000000000000000000000000000000008204169061ffff7c01000000000000000000000000000000000000000000000000000000009091041660408051610100810182526004848116151582526001808616151560208401526008958616151593830193909352938316151560608201526010831615156080820152908216151560a082015260028216151560c08201529116151560e082015290565b8051909150156102235773ffffffffffffffffffffffffffffffffffffffff8981165f90815262721c0960209081526040808320938b1683526002840190915290205460ff16156101c657507f2b635c8800000000000000000000000000000000000000000000000000000000925061026c915050565b73ffffffffffffffffffffffffffffffffffffffff87165f90815260028201602052604090205460ff161561022157507f2a5cb1c300000000000000000000000000000000000000000000000000000000925061026c915050565b505b8060200151610249576102398a8a8a88886102b4565b1561024957505f915061026c9050565b8154610267908290610100900465ffffffffffff168b898c8c6102d7565b925050505b979650505050505050565b5c90565b5f7fdacd49f6a6c42b45a5c3d195b83b324104542d9147bb8064a39c6a8d23ba9b005460ff16156102ab57505c90565b5080545b919050565b5f6102cd858585856102c58b610594565b63ffffffff16565b9695505050505050565b65ffffffffffff85165f9081527fdddabf5c2ba4d65801ab32cdb63ad695571faeb901010108d1a350518718439e602090815260408083207f2841ea725888be53292047902c89cc990fdbb15dbc2e5b558a6a052c722a0ede90925282206060890151600483019260050191901561039257863b15610392576103628a604001518484848c8c6105c2565b61039257507faca58aa00000000000000000000000000000000000000000000000000000000092506102cd915050565b8960800151156103eb576103a587610682565b6103eb576103bb8a604001518484848c8c6105c2565b6103eb57507fce32f2aa0000000000000000000000000000000000000000000000000000000092506102cd915050565b8473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1603610542578960a0015161054257843b15610536575f806104398761073a565b915091508b60c001511561049e5781610499578b60e00151610499576104678c604001518686868e8c6105c2565b61049957507f42d0e0e40000000000000000000000000000000000000000000000000000000094506102cd9350505050565b610533565b81156104e6576104b48c604001518c8c84610762565b61049957507f369c2f4c0000000000000000000000000000000000000000000000000000000094506102cd9350505050565b8b60e00151610533576105018c604001518686868e8c6105c2565b61053357507f42d0e0e40000000000000000000000000000000000000000000000000000000094506102cd9350505050565b50505b505f92506102cd915050565b6105558a604001518484848c8b8b610898565b61058557507fe1f1d02e0000000000000000000000000000000000000000000000000000000092506102cd915050565b505f9998505050505050505050565b610df9600182036105a85750610966919050565b600282036105b957506109d3919050565b506109d3919050565b5f8615610675575f80527f2841ea725888be53292047902c89cc990fdbb15dbc2e5b558a6a052c722a0ede6020527f938cff0dbdc4b4032da5e0e3a4947fb40aba826911a8ad09a80c555fd5830d5b7f938cff0dbdc4b4032da5e0e3a4947fb40aba826911a8ad09a80c555fd5830d5c7fe6367c6c33ddbb3b5b8a8c588253bb03f885a3f85345efae1611865a80acc52361066083838389896109f7565b1561067157600193505050506102cd565b5050505b61026c86868686866109f7565b6040517f89a9c85500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301525f917f000000000000000000000000e0a0004dfa318fc38298ae81a666710eadceba5c909116906389a9c85590602401602060405180830381865afa158015610710573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107349190610e92565b92915050565b5f805f805260175f601d853c62ef01005f510361075d57505060205160019060601c5b915091565b5f8415610815575f80527fe91a6628aa83f010ff9afa7f160b18736f245c1821f24ffae38c046526028eb16020527f739a1fa67523b96335a4175253d835d43315604e3ab95d6deb68d9488a6242f17f739a1fa67523b96335a4175253d835d43315604e3ab95d6deb68d9488a6242f27fa731cdb379c53870826417699a62d8514fe9e04ae3343c38d8e6940d1ccb6bca61080083838389896109f7565b156108115760019350505050610890565b5050505b65ffffffffffff84165f9081527f5e58bed0817b31bdcd1d55e32930f39f8107eac1d4c0fd8dbef0c8c7e7389b6f602090815260408083207fe91a6628aa83f010ff9afa7f160b18736f245c1821f24ffae38c046526028eb190925290912060048201916005019061088a83838389896109f7565b93505050505b949350505050565b5f871561094c575f80527f2841ea725888be53292047902c89cc990fdbb15dbc2e5b558a6a052c722a0ede6020527f938cff0dbdc4b4032da5e0e3a4947fb40aba826911a8ad09a80c555fd5830d5b7f938cff0dbdc4b4032da5e0e3a4947fb40aba826911a8ad09a80c555fd5830d5c7fe6367c6c33ddbb3b5b8a8c588253bb03f885a3f85345efae1611865a80acc5236109378383838a8a8a610b1f565b15610948576001935050505061026c565b5050505b61095a878787878787610b1f565b98975050505050505050565b5f806109878561098288875f9182526020526040902060041c90565b610d2c565b509150811561099a576001915050610890565b6109b485610982885f90815262721c066020526040902090565b90925090508180156102cd575060ff81901c6001149695505050505050565b5f84815262721c06602052604081206109ed908590610d2c565b5095945050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526020869052604081205460ff1680610a395750813f5f9081526020869052604090205460ff165b905080610b16575f610a4a85610db8565b90505f5b81811015610b13575f610a618783610dc1565b6040517fb6b3527200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff888116600483015287811660248301529192509082169063b6b3527290604401602060405180830381865afa158015610ad7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610afb9190610e92565b15610b0a576001935050610b13565b50600101610a4e565b50505b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff82165f9081526020879052604081205460ff1680610b76575073ffffffffffffffffffffffffffffffffffffffff82165f9081526020889052604090205460ff165b80610b905750823f5f9081526020879052604090205460ff165b80610baa5750813f5f9081526020879052604090205460ff165b9050806102cd575f610bbb86610db8565b90505f5b81811015610d20575f610bd28883610dc1565b6040517fb6b3527200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff898116600483015288811660248301529192509082169063b6b3527290604401602060405180830381865afa158015610c48573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c6c9190610e92565b80610d0857506040517fb6b3527200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8881166004830152868116602483015282169063b6b3527290604401602060405180830381865afa158015610ce4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d089190610e92565b15610d17576001935050610d20565b50600101610bbf565b50509695505050505050565b5f80610d5b837f000000000000000000000000000000000000000000000000000001a20000027763ffffffff16565b90508073ffffffffffffffffffffffffffffffffffffffff811660011480610dae57508473ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b9250509250929050565b5f610734825490565b5f610dcc8383610dd3565b9392505050565b5f825f018281548110610de857610de8610eb1565b905f5260205f200154905092915050565b610e01610ede565b565b803573ffffffffffffffffffffffffffffffffffffffff811681146102af575f80fd5b5f805f805f805f60e0888a031215610e3c575f80fd5b87359650610e4c60208901610e03565b9550610e5a60408901610e03565b9450610e6860608901610e03565b9350610e7660808901610e03565b925060a0880135915060c0880135905092959891949750929550565b5f60208284031215610ea2575f80fd5b81518015158114610dcc575f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52605160045260245ffd
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000e0a0004dfa318fc38298ae81a666710eadceba5c
-----Decoded View---------------
Arg [0] : eoaRegistry_ (address): 0xE0A0004Dfa318fc38298aE81a666710eaDCEba5C
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000e0a0004dfa318fc38298ae81a666710eadceba5c
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
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.