Advanced Usage (Going Beyond The Defaults)
The Transfer Validator gives creators complete control over their token ecosystem. This section explores the customization options available to creators.
1. Select A Ruleset
Transfer Validator Version 5 currently includes four ruleset modules.
| Ruleset | Id Binding | Global Options | Ruleset Options | Default Ruleset | Description |
|---|---|---|---|---|---|
| Whitelist | 0, 4 | GO0/1/2/3 | WLO0/1/2/3/4/5 | ✔ | Encompasses Security Levels 3 through 8 from prior validators. Blocks all transfers, unless allowed through an authorizer, a whitelist, or other whitelisting option. |
| Vanilla | 1 | None | None | Equivalent to Security Level 1 from prior validators. Allows all transfers. | |
| Soulbound | 2 | None | None | Equivalent to Security Level 9 from prior validators. Blocks all transfers. | |
| Blacklist | 3 | GO0/1/2/3 | None | Equivalent to Security Level 2 from prior validators. Allows all transfers, unless the operator (msg.sender) is explicitly blocked by address or codehash. |
2. Set Global and Ruleset-Specific Options
Note: By default, all options bits are set to zero (off).
Global Options Table
| Option Code | Option Name | Bit | Rulesets |
|---|---|---|---|
| GO0 | Global Option Disable Authorization Mode | 0 | Whitelist Blacklist |
| GO1 | Global Option No Authorizer Wildcard Operators Mode | 1 | Whitelist Blacklist |
| GO2 | Global Option Account Freezing Mode | 2 | Whitelist Blacklist |
| GO3 | Global Option Default List Extension Mode | 3 | Whitelist Blacklist |
| GO4 | Reserved | 4 | |
| GO5 | Reserved | 5 | |
| GO6 | Reserved | 6 | |
| GO7 | Reserved | 7 |
Ruleset Whitelist Options Table
| Option Code | Option Name | Bit | Rulesets |
|---|---|---|---|
| WLO0 | Whitelist Option Block All OTC | 0 | Whitelist |
| WLO1 | Whitelist Option Allow OTC For 7702 Delegates | 1 | Whitelist |
| WLO2 | Whitelist Option Allow OTC For Smart Wallets | 2 | Whitelist |
| WLO3 | Whitelist Option Block Smart Wallet Receivers | 3 | Whitelist |
| WLO4 | Whitelist Option Block Unverified EOA Receivers | 4 | Whitelist |
| WLO5 | Reserved | 5 | |
| WLO6 | Reserved | 6 | |
| WLO7 | Reserved | 7 | |
| WLO8 | Reserved | 8 | |
| WLO9 | Reserved | 9 | |
| WLO10 | Reserved | 10 | |
| WLO11 | Reserved | 11 | |
| WLO12 | Reserved | 12 | |
| WLO13 | Reserved | 13 | |
| WLO14 | Reserved | 14 | |
| WLO15 | Reserved | 15 |
3. Choose A List, Or Default List Extension
Most rulesets use lists of accounts or codehashes to allow or deny transfers. These lists function much like a firewall with an access control list. This on-chain access control list governs what operators are allowed, etc. Lists are identified and accessed by a key derived from two pieces of information. First, the List Id, is a uint48 value ranging from 0 (Default List Id Managed By Limit Break) to 281,474,976,710,655. Second, the List Type, is a uint8 value ranging from 0 to 255 that partitions the List Id into multiple kinds of lists.
List Ids
| List Id | List Manager | Description |
|---|---|---|
| 0 | Limit Break | Default List |
| 1 - 281,474,976,710,655 | Developers / Creators Exchanges DAOs / Enterprises | Custom / Community Lists |
List Types
| Type Id | List Type | Applicable Ruleset(s) |
|---|---|---|
| 0 | Blacklist | Blacklist |
| 1 | Whitelist | Whitelist |
| 2 | Authorizers | Whitelist Blacklist |
| 3 | Whitelist Extension Contracts | Whitelist |
| 4 | EIP-7702 Delegate Whitelist | Whitelist |
| 5 | EIP-7702 Delegate Whitelist Extension Contracts | Whitelist |
| 6-255 | Reserved for Future Validator Expansion | TBD |
Blacklists
Blacklists, applicable only to the Blacklist ruleset, contain a set of account addresses and codehashes. The blacklist ruleset allows all transfers unless the operator (msg.sender) address is explicitly specified in the blacklist or the codehash of the operator is explicitly specified in the blacklist. Blacklists may extend the default blacklist, in which case the collection owner can specify a custom list id to be combined with the contents of the default blacklist with list id 0.
Whitelists, Whitelist Extension Contracts, and EIP-7702 Delegate Whitelists
Whitelists, applicable only to the Whitelist ruleset, contain a set of account addresses and codehashes that are explicitly allowed to be the operator on transfers, or explicit accounts that can perform OTC transfers in all circumstances, or carveouts to smart contract receiver constraints.
A whitelist extension contract is an external contract that can be queried to perform a check more advanced than a simple account or codehash comparison. For example, lets say that a whitelist should include any smart wallet contract that originated from a specific trusted wallet factory. An extension contract that checks to see if an account address was created by the trusted factory could be deployed and added to the whitelist extension contracts list.
The following table shows how various options block transfers and how whitelists can be applied to allow the transfer in a controlled manner.
| Option | Scenario | Whitelisting Checks |
|---|---|---|
| WLO0 | When Block All OTC is enabled, and caller equals from (token owner calls transfer directly)... | Check if caller (owner/sender) is a whitelisted address. If not, check if codehash of caller is whitelisted. If not, iterate over whitelist extensions and check if caller is whitelisted by any extension in the list. When options include default list extension mode, repeat checks for the default list as well. If all applicable whitelist checks fail, block the transfer. |
| WLO0 | When Block All OTC is enabled, and caller does not equal from (operator/protocol-initiated transfer)... | Check if caller or from (operator/protocol/sender) is a whitelisted address. If not, check if codehash of caller or from is whitelisted. If not, iterate over whitelist extensions and check if caller or from is whitelisted by any extension in the list. When options include default list extension mode, repeat checks for the default list as well. If all applicable whitelist checks fail, block the transfer. |
| WLO0 | When Block All OTC is disabled, and caller does not equal from (operator/protocol-initiated transfer)... | Check if caller (operator/protocol) is a whitelisted address. If not, check if codehash of caller is whitelisted. If not, iterate over whitelist extensions and check if caller is whitelisted by any extension in the list. When options include default list extension mode, repeat checks for the default list as well. If all applicable whitelist checks fail, block the transfer. |
| WLO3 | When Block Smart Wallet Receivers is enabled, and the codelength of to is greater than zero... | Check if to (receiver) is a whitelisted address. If not, check if codehash of to is whitelisted. If not, iterate over whitelist extensions and check if to is whitelisted by any extension in the list. When options include default list extension mode, repeat checks for the default list as well. If all applicable whitelist checks fail, block the transfer. |
| WLO4 | When Block Unverified EOA Receivers is enabled, and the to account has not verified a signature with the EOA registry... | Check if to (receiver) is a whitelisted address. If not, check if codehash of to is whitelisted. If not, iterate over whitelist extensions and check if to is whitelisted by any extension in the list. When options include default list extension mode, repeat checks for the default list as well. If all applicable whitelist checks fail, block the transfer. |
| WLO1 | When Block All OTC is disabled and Allow OTC For 7702 Delegates is disabled, and caller equals from (token owner calls transfer directly), and caller has an EIP-7702 delegation attached... | Get the address of the delegation attached to caller. Check if delegation is a whitelisted address in the delegate whitelist. If not, check if codehash of delegation is whitelisted in the delegate whitelist. If not, iterate over delegate whitelist extensions and check if delegation is whitelisted by any extension in the list. When options include default list extension mode, repeat checks for the default list as well. If all applicable delegate whitelist checks fail, block the transfer. |
| WLO2 | When Block All OTC is disabled and Allow OTC For Smart Wallets is disabled, and caller equals from (token owner calls transfer directly), and the caller has code, but is not an EIP-7702 delegate... | Check if caller (owner/sender) is a whitelisted address. If not, check if codehash of caller is whitelisted. If not, iterate over whitelist extensions and check if caller is whitelisted by any extension in the list. When options include default list extension mode, repeat checks for the default list as well. If all applicable whitelist checks fail, block the transfer. |
Authorizers
Authorizers are trusted smart contracts that have special permissions to override blacklist and whitelist restrictions. Special care must be taken to only add well-trusted authorizers. At the current time, the Seaport Royalty Enforcing Zone is the only contract that implements the required interfaces for an authorizer contract. The Seaport Royalty Enforcing Zone relies on an oracle to examine the royalties included in Seaport orders and sign a message authenticating that royalties are properly included in the order. The Royalty Enforcing Zone verifies the oracles signature, and once authenticated the zone uses authorization functions on the Transfer Validator to temporarily override blacklist or whitelist restrictions to allow Seaport trades that properly include full creator royalties. At the current time, OpenSea and Reservoir operate the two default trusted authorizer zones/oracles.
4. Choose Automatic or Manual Updates
By default, Transfer Validator Version 5 allows Limit Break to upgrade and/or patch rulesets. This allows creators to have improved rulesets automatically applied to their collections without any action on their part. Some creators may prefer not to receive automatic updates. In this case, the creator can specify an exact version of registered ruleset to apply to their collections. If/when Limit Break updates ruleset bindings, collections that have specified an exact version of ruleset will not receive the update without manual action on their part.
5. Apply Desired Settings from Steps 1-4
Limit Break provides a convenient Developer Tools UI to configure validator settings. However, developers can apply desired settings by interacting directly with the contract on Etherscan, My Ether Wallet, Gnosis Safe, etc by calling the following list and collection management functions.
Collection-Specific Function Calls
/**
* @notice Set the ruleset id, global / ruleset options, fixed / custom ruleset for a collection.
*
* @dev Throws when the caller is neither collection contract, nor the owner or admin of the specified collection.
* @dev Throws when setting a custom ruleset to an unregistered ruleset address.
* @dev Throws when setting a ruleset id that is not bound to a ruleset address.
* @dev Throws when setting a custom ruleset with a managed ruleset id.
*
* @dev <h4>Postconditions:</h4>
* 1. The ruleset of the specified collection is set to the new value.
* 2. Global options and ruleset-specific options of the specified collection are set to the new value.
* 3. A `SetCollectionRuleset` event is emitted.
* 4. A `SetCollectionSecurityPolicyOptions` event is emitted.
*
* @param collection The address of the collection.
* @param rulesetId The new ruleset id to apply.
* @param customRuleset The address of the custom ruleset to apply. Must be address(0) unless ruleset
* id is RULESET_ID_FIXED_OR_CUSTOM (255).
* @param globalOptions The global options to apply.
* @param rulesetOptions The ruleset-specific options to apply.
*/
function setRulesetOfCollection(address collection, uint8 rulesetId, address customRuleset, uint8 globalOptions, uint16 rulesetOptions) external;
/**
* @notice Applies the specified list to a collection.
*
* @dev Throws when the caller is neither collection contract, nor the owner or admin of the specified collection.
* @dev Throws when the specified list id does not exist.
*
* @dev <h4>Postconditions:</h4>
* 1. The list of the specified collection is set to the new value.
* 2. An `AppliedListToCollection` event is emitted.
*
* @param collection The address of the collection.
* @param id The id of the operator whitelist.
*/
function applyListToCollection(address collection, uint48 id) external;
Creating and Managing Custom Lists
/**
* @notice Creates a new list id. The list id is a handle to allow editing of blacklisted and whitelisted accounts
* and codehashes.
*
* @dev <h4>Postconditions:</h4>
* 1. A new list with the specified name is created.
* 2. The caller is set as the owner of the new list.
* 3. A `CreatedList` event is emitted.
* 4. A `ReassignedListOwnership` event is emitted.
*
* @param name The name of the new list.
* @return id The id of the new list.
*/
function createList(string calldata name) external returns (uint48 id);
/**
* @notice Creates a new list id, and copies all blacklisted and whitelisted accounts and codehashes from the
* specified source list.
*
* @dev <h4>Postconditions:</h4>
* 1. A new list with the specified name is created.
* 2. The caller is set as the owner of the new list.
* 3. A `CreatedList` event is emitted.
* 4. A `ReassignedListOwnership` event is emitted.
* 5. All blacklisted and whitelisted accounts and codehashes from the specified source list are copied
* to the new list.
* 6. An `AddedAccountToList` event is emitted for each blacklisted and whitelisted account copied.
* 7. An `AddedCodeHashToList` event is emitted for each blacklisted and whitelisted codehash copied.
*
* @param name The name of the new list.
* @param sourceListId The id of the source list to copy from.
* @return id The id of the new list.
*/
function createListCopy(string calldata name, uint48 sourceListId) external returns (uint48 id);
/**
* @notice Creates a new list id, and copies all accounts and codehashes from the
* specified source list for each specified list type.
*
* @dev <h4>Postconditions:</h4>
* 1. A new list with the specified name is created.
* 2. The caller is set as the owner of the new list.
* 3. A `CreatedList` event is emitted.
* 4. A `ReassignedListOwnership` event is emitted.
* 5. All accounts and codehashes from the specified source list / list types are copied
* to the new list.
* 6. An `AddedAccountToList` event is emitted for each account copied.
* 7. An `AddedCodeHashToList` event is emitted for each codehash copied.
*
* @param name The name of the new list.
* @param sourceListId The id of the source list to copy from.
* @param listTypes The list types to copy from the source list.
* @return id The id of the new list.
*/
function createListCopy(string calldata name, uint48 sourceListId, uint8[] calldata listTypes) external returns (uint48 id);
/**
* @notice Transfer ownership of a list to a new owner.
*
* @dev Throws when the new owner is the zero address.
* @dev Throws when the caller does not own the specified list.
*
* @dev <h4>Postconditions:</h4>
* 1. The list ownership is transferred to the new owner.
* 2. A `ReassignedListOwnership` event is emitted.
*
* @param id The id of the list.
* @param newOwner The address of the new owner.
*/
function reassignOwnershipOfList(uint48 id, address newOwner) external;
/**
* @notice Renounce the ownership of a list, rendering the list immutable.
*
* @dev Throws when the caller does not own the specified list.
* @dev Throws when list id is zero (default list).
*
* @dev <h4>Postconditions:</h4>
* 1. The ownership of the specified list is renounced.
* 2. A `ReassignedListOwnership` event is emitted.
*
* @param id The id of the list.
*/
function renounceOwnershipOfList(uint48 id) external;
/**
* @notice Adds one or more accounts to a list of specified list type.
*
* @dev Throws when the caller does not own the specified list.
* @dev Throws when the accounts array is empty.
*
* @dev <h4>Postconditions:</h4>
* 1. Accounts not previously in the list are added.
* 2. An `AddedAccountToList` event is emitted for each account that is newly added to the list.
*
* @param id The id of the list.
* @param listType The type of the list.
* @param accounts The addresses of the accounts to add.
*/
function addAccountsToList(uint48 id, uint8 listType, address[] calldata accounts) external;
/**
* @notice Removes one or more accounts from a list of the specified list type.
*
* @dev Throws when the caller does not own the specified list.
* @dev Throws when the accounts array is empty.
*
* @dev <h4>Postconditions:</h4>
* 1. Accounts previously in the list are removed.
* 2. A `RemovedAccountFromList` event is emitted for each account that is removed from the list.
*
* @param id The id of the list.
* @param listType The type of the list.
* @param accounts The addresses of the accounts to remove.
*/
function removeAccountsFromList(uint48 id, uint8 listType, address[] calldata accounts) external;
/**
* @notice Adds one or more codehashes to a list of specified list type.
*
* @dev Throws when the caller does not own the specified list.
* @dev Throws when the codehashes array is empty.
* @dev Throws when a codehash is zero.
*
* @dev <h4>Postconditions:</h4>
* 1. Codehashes not previously in the list are added.
* 2. An `AddedCodeHashToList` event is emitted for each codehash that is newly added to the list.
*
* @param id The id of the list.
* @param listType The type of the list.
* @param codehashes The codehashes to add.
*/
function addCodeHashesToList(uint48 id, uint8 listType, bytes32[] calldata codehashes) external;
/**
* @notice Removes one or more codehashes from a list of the specified list type.
*
* @dev Throws when the caller does not own the specified list.
* @dev Throws when the codehashes array is empty.
*
* @dev <h4>Postconditions:</h4>
* 1. Codehashes previously in the list are removed.
* 2. A `RemovedCodeHashFromList` event is emitted for each codehash that is removed from the list.
*
* @param id The id of the list.
* @param listType The type of the list.
* @param codehashes The codehashes to remove.
*/
function removeCodeHashesFromList(uint48 id, uint8 listType, bytes32[] calldata codehashes) external;
Checking List and Collection Settings
/**
* @notice Returns the owner of the specified list id.
*/
function listOwners(uint48 id) external view returns (address);
/**
* @notice Get the security policy of the specified collection.
* @param collection The address of the collection.
* @return The security policy of the specified collection, which includes:
* Ruleset id, list id, global options, ruleset-specific options, optional custom ruleset address,
* and token type (if registered).
*/
function getCollectionSecurityPolicy(address collection) external view returns (CollectionSecurityPolicy memory);
/**
* @notice Get accounts by list id and list type.
* @param id The id of the list.
* @param listType The type of the list.
* @return An array of accounts in the list of the specified type.
*/
function getListAccounts(uint48 id, uint8 listType) external view returns (address[] memory);
/**
* @notice Get codehashes by list id and list type.
* @param id The id of the list.
* @param listType The type of the list.
* @return An array of codehashes in the list of the specified type.
*/
function getListCodeHashes(uint48 id, uint8 listType) external view returns (bytes32[] memory);
/**
* @notice Check if an account is found in a specified list id / list type.
* @param id The id of the list.
* @param listType The type of the list.
* @param account The address of the account to check.
* @return True if the account is in the specified list / type, false otherwise.
*/
function isAccountInList(uint48 id, uint8 listType, address account) external view returns (bool);
/**
* @notice Check if a codehash is in a specified list / type.
* @param id The id of the list.
* @param listType The type of the list.
* @param codehash The codehash to check.
* @return True if the codehash is in the specified list / type, false otherwise.
*/
function isCodeHashInList(uint48 id, uint8 listType, bytes32 codehash) external view returns (bool);
/**
* @notice Get accounts in list by collection and list type.
* @param collection The address of the collection.
* @param listType The type of the list.
* @return An array of accounts.
*/
function getListAccountsByCollection(address collection, uint8 listType) external view returns (address[] memory);
/**
* @notice Get codehashes in list by collection and list type.
* @param collection The address of the collection.
* @param listType The type of the list.
* @return An array of codehashes.
*/
function getListCodeHashesByCollection(address collection, uint8 listType) external view returns (bytes32[] memory);
/**
* @notice Check if an account is in the list by a specified collection and list type.
* @param collection The address of the collection.
* @param listType The type of the list.
* @param account The address of the account to check.
* @return True if the account is in the list / list type of the specified collection, false otherwise.
*/
function isAccountInListByCollection(address collection, uint8 listType, address account) external view returns (bool);
/**
* @notice Check if a codehash is in the list by a specified collection / list type.
* @param collection The address of the collection.
* @param listType The type of the list.
* @param codehash The codehash to check.
* @return True if the codehash is in the list / list type of the specified collection, false otherwise.
*/
function isCodeHashInListByCollection(address collection, uint8 listType, bytes32 codehash) external view returns (bool);
