Tags
The canonical form every tag is reduced to before it means anything.
A tag has exactly one canonical form. Given raw input, normalization does
two things and two things only: it strips a single leading @ if present,
and it lowercases the result. Everything else is a rejection, not a
transform.
To be accepted, the result must be:
- 3 to 20 characters long
- composed only of lowercase ASCII letters, digits, and underscore
(
a-z,0-9,_)
@Daniel_99 normalizes to daniel_99. dan.eth is rejected outright
(. is not an allowed character), it does not become daneth.
Why ASCII-only
Restricting tags to ASCII rules out homoglyph attacks: Unicode has many
characters from other scripts that render as visually identical, or nearly
identical, to a plain ASCII letter (a Cyrillic а next to a Latin a, for
example). Without this restriction, someone could register a tag that
looks like an existing one to a human eye but is a different address
underneath, a classic phishing setup. Keeping the character set to
a-z0-9_ closes that off entirely rather than trying to detect
confusable characters after the fact.
Client normalizes, program validates
Normalization (stripping @, lowercasing) happens once, client-side, in
@tip/core. The on-chain program never transforms input: it checks that a
tag is already exactly in canonical form, and rejects anything that is not,
byte for byte. This split means the program's validation logic can be a
straightforward, non-negotiable gate, while the friendlier "let the user
type @Daniel" experience lives entirely in the client.