Username Rules
This guide will explain Username Rules and how to implement them.
Username Rules allow Username namespaces' administrators to add requirements or constraints that will be applied when a Username is created or when it is assigned to an account.
See the Rules concept section to learn more about them.
Existing Username Rules
CharsetUsernameRule
This rule can be applied to a Username namespace to restrict the set of characters that can be used for the Usernames created under the context of that namespace.
This rule applies constraints only upon creation of Usernames and can be configured to simultaneously:
Allow or disallow numeric characters.
Allow or disallow latin lowercase characters.
Allow or disallow latin uppercase characters.
Allow a custom set of characters.
Disallow a custom set of characters.
Disallow a custom set of characters to not be used as starting character.
LengthUsernameRule
This rule can be applied to a Username namespace to restrict the length of the Usernames created under the context of that namespace.
This rule applies constraints only upon creation of Usernames and allows to configure both minimum and maximum length restrictions. Each of these restrictions are represented by an 8-bit unsigned integer value, and can be set as zero if any of the restrictions is not desired.
SimplePaymentUsernameRule
This rule can be applied to a Username namespace to require an ERC-20 payment in order to create or assign a Username under the context of that namespace.
This rule can be configured to apply the payment requirement upon creation of Usernames, assignment of Usernames, or both.
The configuration also requires the ERC-20 token address, the amount of tokens required for the payment, and the recipient address where the tokens will be sent.
TokenGatedUsernameRule
This rule can be applied to a Username namespace to require an account to hold a certain balance of a token (both fungible and non-fungible are supported) in order to successfully create or assign a Username under the context of that namespace.
This rule can be configured to apply the token gate upon creation of Usernames, assignment of Usernames, or both.
The configuration also requires the address of the token, the token standard used by the provided token (ERC-20, ERC-721, and ERC-1155 are supported), and the amount of tokens required to pass the gate. In case of using the ERC-1155 token standard, an additional token type ID is expected to be provided.
Building a Username Rule
Let's illustrate the process with an example. We will build a custom Username Rule that requires Usernames to be created only if their length has an specific parity, for example, all usernames must have an even length.
To build a custom Username Rule, you must implement the following IUsernameRule interface:
interface IUsernameRule { function configure(bytes calldata data) external;
function processCreation( address account, string calldata username, bytes calldata data ) external returns (bool);
function processAssigning( address account, string calldata username, bytes calldata data ) external returns (bool);}
Each function of this interface must assume to be invoked by the Username namespace contract.
A Lens dependency package with all relevant interfaces will be available soon.
First, implement the configure function. This function has the purpose of initializing any required state for the rule to work properly.
The configure function can be called multiple times by the same Username namespace in order to update the rule configuration (i.e. reconfigure it).
It receives bytes that will be decoded into the required rule configuration parameters.
In our example, we only need to decode a boolean parameter, which will indicate if the rule will enforce Usernames to have an even or an odd length. Let's define a storage mapping to store this configuration:
contract ParityLengthUsernameRule is IUsernameRule {
mapping(address => bool) internal _mustBeEvenLength;}
Now let's code the configure function itself, decoding the boolean parameter and storing it in the mapping:
contract ParityLengthUsernameRule is IUsernameRule {
mapping(address => bool) internal _mustBeEvenLength;
function configure(bytes calldata data) external override { _mustBeEvenLength[msg.sender] = abi.decode(data, (bool)); }}
The configuration is stored in the mapping using the Username namespace contract address as the key, which is the msg.sender. So the same rule can be reused by different Username contracts.
Next, implement the processCreation function. This function is invoked by the Username namespace contract every time a username is being created, so then our custom logic can be applied to shape under which conditions this operation can succeed.
The function receives the account creating the Username, the username being created, and some data in case the rule requires additional information to work.
The function must revert in case of not meeting the requirements imposed by the rule. Otherwise, it must return a boolean that indicates if the rule is applying a restriction over this operation or not. In our example, given that a restriction is indeed being applied to the creation, we will return true.
contract ParityLengthUsernameRule is IUsernameRule {
mapping(address => bool) internal _mustBeEvenLength;
// . . .
function processCreation( address account, string calldata username, bytes calldata data ) external view override returns (bool) { // We get the length of the username being created uint256 usernameLength = bytes(username).length;
// We check if the length is even (otherwise it is odd) bool isEvenLength = usernameLength % 2 == 0;
// We require the parity of the username being created to match // the parity required by the rule require(isEvenLength == _mustBeEvenLength[msg.sender]);
// We return true to signal that the rule is // being applied when creating a Username return true; }}
Finally, implement the processAssigning function. This function is invoked by the Username namespace contract every time a username is being assigned to an account, so then our rule can define if this operation must succeed or not.
The function receives the account the Username will be assigned to, the username to assign, and some data in case the rule requires additional information to work.
The function must revert in case of not meeting the requirements imposed by the rule. Otherwise, it must return a boolean that indicates if the rule is applying a restriction over this operation or not. In our example, we do not want to apply the restriction to the assigning operation, so we must implement the function to comply with the interface but just return false.
contract ParityLengthUsernameRule is IUsernameRule {
mapping(address => bool) internal _mustBeEvenLength;
// . . .
function processAssigning( address account, string calldata username, bytes calldata data ) external view override returns (bool) { // We return false to signal that we do not // apply the rule when assigning a Username return false; }}
Now the ParityLengthUsernameRule is ready to be applied into any Username. See the full code below:
contract ParityLengthUsernameRule is IUsernameRule {
mapping(address => bool) internal _mustBeEvenLength;
function configure(bytes calldata data) external override { _mustBeEvenLength[msg.sender] = abi.decode(data, (bool)); }
function processCreation( address account, string calldata username, bytes calldata data ) external view override returns (bool) { // We get the length of the username being created uint256 usernameLength = bytes(username).length;
// We check if the length is even (otherwise it is odd) bool isEvenLength = usernameLength % 2 == 0;
// We require the parity of the username being created to match // the parity required by the rule require(isEvenLength == _mustBeEvenLength[msg.sender]);
// We return true to signal that the rule is // being applied when creating a Username return true; }
function processAssigning( address account, string calldata username, bytes calldata data ) external view override returns (bool) { // We return false to signal that we do not // apply the rule when assigning a Username return false; }}
Stay tuned for API integration of rules and more guides!