# FIP: ENS Support in Hubs Source: https://github.com/farcasterxyz/protocol/discussions/90 Kiwi News: https://news.kiwistand.com/stories/FIP-ENS-Support-in-Hubs?index=0x647797228fdfe257a1c99a9db5412868eaad554f654fe67598c0654224bfe1c8c5d474e0 Submitted by tim on 2023-05-31 UTC, 1 upvote, 0 comments ## Summary Farcaster's FIP "ENS Usernames" proposes a two-tier naming system in which users choose between a free off-chain ENS name (fname) moderated by the Farcaster team or a decentralized on-chain ENS name that costs gas. Fnames are issued under the fcast.id ENS namespace via CCIP by a nameserver at fnames.facaster.xyz, and users can claim one by submitting an EIP-712 signed message proving ownership of an fid. Hubs will track valid usernames, re-validate ownership, and emit events when usernames change, and a validation job runs at 2am UTC to revoke USER_DATA.USERNAME messages that are no longer correctly owned. ## Article Title: ENS Usernames Type: Implementation FIP Authors: @sanjay, @sds, @v, @horsefacts Abstract Farcaster supports ENS names issued on on-chain or off-chain. User can choose between free, off-chain ENS names (fnames) or on-chain decentralized ENS names, making registration less expensive and much faster. Applications can rely on Hubs to validate ownership of ENS names and let them know when ownership changes. Problem Farcasters issues human-readable usernames called fnames which can be used in applications. New users must make two on-chain transactions to acquire the name, and sign an off-chain message to instruct apps to use this name as their preferred name. There are a few problems with this system: Acquiring an fname or ens requires a slow and expensive transaction on Ethereum L1 Fnames aren’t compatible with the ENS ecosystem, which has a lot of great tooling. Applications must write custom code to resolve ENS names for users. We want to design a system that is: Low Friction — users must be able to sign up and acquire a free and unique username. Decentralized — users should be able to upgrade to a decentralized name (ENS). Simple — should be fast to implement and unblock developers who want to sign up users. Trustworthy — should make it hard to abuse usernames and impersonate others. ENS Compatibility — should be compatible with ENS to benefit from the ecosystem. Specification We propose a two-tier naming system where users can choose between: An off-chain ENS name (fname) that is free but moderated by the Farcaster team. An on-chain ENS name that costs gas but is fully decentralized and under the user’s control. Overview Users can own many types of ENS names in their custody and verified addresses, including L1 names and off-chain names. Users can prove ownership of these names to a Hub by submitting a UsernameProof message and select a primary name by submitting a USER_DATA.USERNAME message, which applications should respect. Off-Chain ENS Names (Fnames) Fnames are free, ENS-compliant names that are issued to all users who own an fid. They are issued under the fcast.id ENS namespace using CCIP by a server operated by the Farcaster team. Applications should prefix off-chain names with an @ and can truncate the domain name for brevity — e.g. @username instead of @username.fcast.id. On-Chain ENS Names ENS names owned on Ethereum L1 can be used as valid Farcaster usernames as long as they meet certain criteria for length and characters used. Applications should prefix ENS usernames with a @ like @username.eth and while preserving the TLD label to avoid collisions. Managing Usernames Hubs keep track of valid usernames for a given user and emit events when ownership of usernames change. This makes it easy for applications to quickly determine what the correct username is without having to write their own resolvers. Fname Specification ENS CCIP Contract A CCIP ENSIP-10 contract will be deployed on L1 which resolves *.fcast.id names to owner addresses. It stores the URL of the nameserver and validates signatures provided by the nameserver. This resolver will support addr record lookups only. The address of the contract is ______ (to be filled on deployment). Name Server The server which resolves *.fcast.id names lives at fnames.facaster.xyz. Fnames can be claimed by submitting an EIP-712 signed message that proves ownership of an fid that does not yet have an fname. The server also provides a method to transfer fnames to other fids by proving ownership of the fname. Usernames are also valid subdomains (e.g. foo.fcast.id ) though they do not currently resolve to anything. A future upgrade to the nameserver may allow the owner to set a redirect record here. The following usernames are not available for registration, since they collide with existing subdomains — www, fnames Managing Fname Ownership A POST request to the /transfers endpoint can be made register, move or deregister a username. The request body must contain : { "from": " // 0 for registering a new fname "to": // 0 for unregistering an existing fname "name": "", // fname "timestamp": // Second resolution "owner": "
" // ETH custody address of the non-zero "from"/"to" fid as of timestamp "signature": "" // hex EIP-712 signature signed by the "owner" address } The request is rejected unless it meets the following criteria: The fname is owned by the “from” fid or is not owned by anyone. The “to” fid does not currently own a username. The name matches the regular expression /^[a-z0-9][a-z0-9-]{0,15}$/. The timestamp is ≤ current time + 1 minute (for clock skew). The owner must be the address that owns the “from” fid, if the “from” fid is not 0. the address that owns the “to” fid, if the “from” fid is 0. a privileged admin address The signature is a valid EIP-712 message from the “owner” which contains the name, timestamp and owner properties. If there exists an existing proof for the fid, the timestamp of this message must be 2419200 seconds (28 days) ahead of that timestamp to prevent abuse. i.e. an fid can only change their name once every 28 days The domain and types for the EIP-712 signature are described below: const domain = { name: 'Farcaster name verification', version: '1', chainId: 1, verifyingContract: '0xe3be01d99baa8db9905b33a3ca391238234b79d1', // name registry contract, will be the farcaster ENS CCIP contract later }; const types = { UserNameProof: [ { name: 'name', type: 'string' }, { name: 'timestamp', type: 'uint256' }, { name: 'owner', type: 'address' } ] }; Verifying Fname Ownership Anyone can verify that a user requested verification of a name by making a call to the server. usrs can make a GET request to /transfers which returns a paginated list of events with the following schema: { "transfers": [ { "id": 1, "from": 0, "to": 1, "username": "test", "timestamp": 1686680932, "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", // EIP-712 signature signed by the server's key "server_signature": "0x68a1a565f603b9966f228a38d918c12f166650749359fe41e2755fabe016026b361dd7d5f917c6f8a09241b29085fbaefffb75e443a3851be85c8b53b", // Original user provided signature "user_signature": "0xf603b9966f228a38d918c12f166650749359fe41e2755fabe016026b361dd7d5f917c6f8a09241b29085fbaefffb75e443a3851be85c8b53b691536d1c", }, // ... ] } Results can be filtered with these query string parameters: from_id= // minimum id from_ts= // minumum timestamp fid= // filter events by a particular fid name= // filter events for a particular name Nameserver Keypair The nameserver maintains its own ECDSA keypair to counter-sign messages or perform administrative actions. The server_signature will be signed by this key. The public key used to perform these signers can be fetched by performing a GET on /signer which returns: { "address": "" // Public address for the server's signer } Hub Modifications Hubs must keep track of valid usernames proofs and a user’s preferred usernames. They must also continuously re-validate ownership of the username and gossip state to each other. They also expose APIs and emit events with the state of those names change. Fname Proofs must be fetched in real-time from the Fname server by the Hubs ENS Proofs must be submitted by users to the Hubs. UserData Message The USER_DATA.FNAME type is renamed to USER_DATA.USERNAME UserDataAddBody validation logic for USER_DATA.USERNAME must validate that a Username Proof exists for this username and this fid, otherwise the message is not accepted. If a Username Proof is removed or evicted for any reason, then any UserDataAddBody with the same name and fid must be evicted. If no value is set for the username, applications should default to showing the display name, profile picture and fid to represent that user. Username Proof Message A Username Proof demonstrates that a user owns a particular ENS name. The proof is established by creating a signature from an Ethereum address or Farcaster Signer along with additional metadata about the name and Farcaster user. message MessageData { .... oneof body { ... UserNameProofBody proof_body = 8; .... } } enum MessageType { ... MESSAGE_TYPE_USERNAME_PROOF = 12; ... } enum UserNameType { USERNAME_TYPE_NONE = 0; USERNAME_TYPE_ENS_FNAME = 1; USERNAME_TYPE_ENS_L1 = 2; } message UserNameProofBody { uint64 timestamp = 1; bytes name = 2; bytes owner = 3; bytes signature = 4; uint64 fid = 5; UserNameType type = 6; } A UsernameProofBody VerificationRemoveBody in a message m is valid only if it passes these validations: timestamp must be ≤ 10 mins ahead of current time fid must be the fid that is claiming ownership of the name If the type is ENS_FNAME: name must match the regular expression /^[a-z0-9][a-z0-9-]{0,15}$/. owner must be the custody address of the fid. signature must be a valid ECDSA signature on the EIP-712 Username Proof message from the owner or the public key of the fname server, which is used for administrative actions. If the type is ENS_L1 : name must be a valid ENS name name must be a valid, un-expired ENS name name must end with .eth The ens name must resolve to the owner signature must be signed by the owner. The domain must match the regular expression /^[a-z0-9][a-z0-9-]{0,15}$/. owner must be a valid eth address. user must have a valid verification for owner signature must be a valid EIP-712 Username Proof signature UsernameProof CRDT The UsernameProof CRDT validates and accepts UsernameProof messages. The CRDT also ensures that a UsernameProof message m passes these validations: m.signer must be a valid Signer in the add-set of the Signer CRDT for message.fid A conflict occurs if two messages that have the same value for m.name. Conflicts are resolved with the following rules: If m.data.timestamp values are distinct, discard the message with the lower timestamp. If m.data.timestamp values are identical, discard the message with the lower fid The UsernameProof CRDT has a per-user size limit of 10. Events A MergeUsernameProofBody must be emitted whenever a new name is merged into the CRDT. enum HubEventType { ... HUB_EVENT_TYPE_MERGE_USERNAME_PROOF = 6; } message HubEvent { oneof body { ... MergeUserNameProofBody merge_username_proof_body = 8; } } message MergeUserNameProofBody { UserNameProofBody username_proof = 1; } API’s Hubs must expose a GetUsernameProof endpoint which returns a proof associated with a username. message UserNameProofRequest { bytes name = 1; } messsage UserNameProofsResponse { repeated UserNameProofBody usernameProofs = 1; } service HubService { ... rpc GetUserNameProof(UserNameProofRequest) returns (UserNameProof); rpc GetUserNameProofsByFid(FidRequest) returns (UserNameProofsResponse); ... } Gossip Changes Hubs must gossip all messages of type USRENAME_TYPE_ENS_L1 but not messages of type USERNAME_TYPE_ENS_FNAME. Name Validation Sync A job is run periodically at 2am UTC to revalidate the ownership of all ENS L1 names and Fnames, and revokes USER_DATA.USERNAME messages if they aren’t correctly owned. Release Off-chain name changes will be included as part of the 2023.7.12 protocol upgrade but the changes are backwards compatible and Hubs can safely launch the updates ahead of this date, but all Hubs must launch the update by this date. On week of June 19, fhe fname server will go live importing existing events from the fname contract, and will accept transfer requests only from admin accounts. Upgraded Hubs can fetch and store username proofs from the fname server. (patch release 1.3.1) The protocol will then transition from mirroring the contract to requiring registrations to go through the fname server instead: On week of June 26, in version 1.3.3, Hubs will validate UserDataType.FNAME against UserNameProof instead of NameRegistryEvent, though registration of fnames will still happen through the contract (patch release again) External clients that depend on NameRegistryEvents (RPC calls, hub event subscriptions) must switch to UserNameProofs. On week of July 3, Warpcast will stop registering new accounts through the fname contract and will register them via the server instead. The fname server now allows any user with an fid to acquire an fname and is “permissionless”. Once fid registration is permissionless, apps can then signup users themselves, which we expect by end of year. This will be accompanied by a patch release 1.3.4 that will add support for ENS names and have the following breaking changes UserDataType.FNAME will be renamed to UserDataType.USERNAME and HubEventType.MERGE_NAME_REGISTRY_EVENT will no longer be emitted (migrate to HubEventType.MERGE_USERNAME_PROOF) Once this feature is live, and all hubs are upgraded, when a new UserDataAdd message of type USER_DATA_TYPE_USERNAME is submitted, the hub will fetch the UserNameProof for the username and only merge the message if a valid proof exists matching the current custody address of the fid submitting the message. At this point, NameRegistryEvents are deprecated. Appendix: Fname Ownership Policy Usernames are free to register and governed by a simple policy that prevents squatting and impersonation. The policy has two tenets: If you register an fname connected to a well-known public person or entity, your name may be deregistered. (e.g. @google) If don't actively use an fname for 60+ days, your name may be de-registered at our discretion. Conflicts that arise will be handled with human intervention from the core team, since isn’t a way to automate this. For instance, you register @elon and Elon Musk signs up after you and wants the name. In such a case, we would ask three questions that guide the decision: Is the user active on Farcaster? (e.g. they've made high quality posts in the last 60 days) Does the user have a reasonable claim to the name? (e.g. their name also Elon) Does they have similar, active handles? (e.g. they own elon.ens and elon on twitter) This policy is will evolve outside of the scope of this FIP, and the latest version will be hosted with the fname repository.