Users & Identity
An identity on Buzzer is a uid — a plain incrementing number (the first uid is 1).
It is not an NFT and not an address; it is a durable account that one or more addresses
act for. This indirection is the heart of the model: an address acts on behalf of a
uid, it does not own one, so keys can be added, rotated and recovered without ever
changing the identity.
Addresses and their roles
Every uid has exactly one custody address and any number of other addresses attached to it. What an address can do is decided by the roles it holds.
| Role | How many | What it can do |
|---|---|---|
| Custody | exactly one | Full control. Implicitly holds every permission; can transfer or delete the profile, add and remove keys, set recovery and payout. |
| Recovery | zero or one | One power only: start a recovery to a new custody (7-day timelock). Exempt from the one-profile-per-address rule, so a single hardware wallet can guard many profiles. |
| Linked address | any number | An address granted a specific set of permission bits. One profile per address. |
Permission bits
A linked address holds a 256-bit permission bitmap. Custody grants and revokes these freely; a linked key can never hold the full custody bit.
| Bit | Name | Grants |
|---|---|---|
| 0 | VERIFIED |
"This wallet belongs to this profile" — login / attribution. No on-chain power by itself. |
| 1 | POST |
buzz, communityBuzz, deleteBuzz, registerMedia |
| 2 | REACT |
react (tips are pulled from the acting address) |
| 3 | GRAPH |
setRelationships, setMemberEdge |
| 4 | PROFILE |
setProfileData |
| 5 | COMMUNITY |
create communities and act as owner/admin/member |
| 6 | MESSAGE |
sendMessage (the on-chain messaging tier) |
| 7–255 | reserved | — |
Adding a key takes the address's consent: either addLink with an EIP-712
LinkConsent signed by that address (bound to the current custody, so a consent given to
one owner can't be redeemed by the next), or a two-step proposeLink / acceptLink with
a 7-day validity window. Any linked address can detach itself with unlinkSelf.
There is no hard cap on keys or communities per profile — the counters are uint24
(max 16,777,215 each), which is a storage width, not a policy limit.
Custody transfer & recovery
Both paths that change the custody key are two-step, so nothing can be forced onto an address:
- Transfer — custody calls
transferCustody, then the new address mustacceptCustody. It must not already be linked elsewhere. Recovery, pending recovery and payout are cleared; existing linked keys are kept for the new owner to review. - Recovery — the recovery address calls
startRecovery(newCustody), which sets a 7-day timelock. During the window custody cancancelRecoveryand rotate the guardian (it sees theRecoveryStartedevent). After the delay, the new custody itself callsexecuteRecovery. This protects a lost custody key, while the timelock protects against a stolen recovery key.
useNonce burns the caller's current EIP-712 nonce, invalidating any signed-but-unsubmitted
Execute or LinkConsent message.
User names (handles)
A handle is one globally-unique name per uid — 1 to 20 characters, [a-z0-9] only,
stored as a right-padded bytes32 (so it reads directly on-chain, no hash lookup).
setName(custody only) paysNAME_FEEby length and releases the old name immediately. A first name is never rate-limited; every later change is subject to a 30-day interval.clearNameis free and respects the same interval.- Pricing rewards longer names: 1 char = 1 ETH, 2 = 0.1, 3 = 0.01, 4 = 0.001, 5–16 = 0.0001 ETH, 17–20 = free.
- The name follows the uid through custody transfer and recovery, and is released on
deleteProfile.
Community handles live in a separate namespace, so a user and a community may share the same name.
Profile data
Profile fields are an open map, key → value bytes, written with setProfileData
(requires PROFILE). Keys are right-padded ASCII conventions — name, bio, pfp,
cover — that the contract stores but never interprets. An empty value clears a key.
Only ProfileDataSet(uid, key) is emitted; the value is read from storage.
Creating a profile
createProfile(recovery, name, referrerUid, keys[], values[]) mints the next uid, makes
the caller the custody address, and optionally sets the name and initial profile data in
one call. It costs PROFILE_FEE (0.0004 ETH) plus any name fee, paid exact-match and
pushed instantly to the treasury. The referrerUid is emitted once in
ProfileCreated and never stored — referral rewards are an off-chain concern that reads
that event.
Genesis invite gate
A deploy-time option makes the first users invite-only. While active, plain
createProfile reverts InviteRequired and users call createProfileInvited with a
bearer invite code. The gate is driven by an immutable signer address and can only ever
open, never re-close or re-key — it lifts forever once either 1,000 profiles exist or a
fixed time window (20 days on mainnet) passes, whichever comes first. The time fallback
means a lost signer key can never brick onboarding. address(0) disables the gate
entirely (an open network from block zero). See the contract API for
the full redemption flow.
Deleting a profile
deleteProfile (custody only) requires that all linked keys are removed and all
communities transferred first, so nothing can act as a dead uid and no community handle is
ever frozen. Custody, recovery and payout are zeroed and the name released.