Public social recipients
A social-account escrow hides which account a coin's fees are for. Some creators want the opposite: a coin that openly says "fees go to @alice", so traders can check it. A public social recipient does exactly that. The account is public and verifiable, but the wallet that finally receives the money stays hidden from traders, from the creator and from the platform.
| Stealth claim kit | Private social escrow | Public social recipient | |
|---|---|---|---|
| Who the fees are for | Hidden | Hidden from the chain (attestors know) | Public and verifiable |
| The recipient's wallet | Hidden | Hidden | Hidden |
| One account across coins | No (one key per coin) | No (one escrow per coin) | Yes: every coin naming @alice shares one account |
| Per-account totals | None | None | Public |
| How the recipient claims | Claim link | Social login and bind | Social login and bind |
| Exit | Direct or shielded | Direct or shielded | Shielded only (Railgun) |
How it works
It is the same KIND_HANDLE FeeVault account as a private escrow, with two differences:
- Deterministic commitment. The commitment's salt and nonce are fixed protocol constants
instead of a secret salt and a random nonce:
handleCommitment = keccak256(abi.encode(platformId, keccak256(userId), PUBLIC_HANDLE_SALT, PUBLIC_HANDLE_NONCE)). The beneficiary id therefore depends only on the account and the fallback, so anyone can recompute "@alice's account" (publicHandleIdin the SDK). Every coin naming her pays into the same id. - Public hint. The launch's
beneficiaryHintis plaintext rather than encrypted. It holds the platform, the numeric user id, the display handle and an attestor's EIP-712 signature over that label (PublicHandle(platformId, userId, handle, issuedAt)). The id commits only to the numeric id. The signature stops a creator from pairing someone's id with another person's@handle.
The label needs a handle lookup, so POST /v1/public-handle is paid with x402 exactly like a handle
commitment: same price and the same generic payment description. A public label's payment
therefore looks like any other lookup.
No contract changes are involved. Registration, bind, timelock, veto, fallback, consolidation and shield templates all work as for any handle escrow.
Claiming without revealing the wallet
- Bind. The account owner logs in through the bind portal, and the browser makes a fresh
stealth key. Attestors sign
BindOwnerfor that key, and the timelock runs as usual. The key is public inBindProposedand therefore tied to the handle. It never holds funds and never sends a transaction; it only signs. - Exit. Relayers and the web app only offer consolidate to USDT, then shield to Railgun. A direct claim to an address would publish the wallet behind a public name, so the relayer refuses it. The person's 0zk address and everything after the shield stay hidden.
Because exits must go through consolidation, public-recipient coins credit fees in the paired
(quote) token only (FEE_IN.Paired).
Keeping private escrows private
The same person may also have private escrows from other coins. Those must never be linked to the public account, so:
- Public totals use the public id only. Private escrows have a random nonce, so their ids can never equal, or be derived from, the public id.
- Separate binds with separate keys. Attestors refuse a bind request that mixes public and
private escrows. They also refuse a stealth key that was already used for the other kind. The
bind portal therefore binds them in two passes, each with its own fresh key and login.
Otherwise one
newOwnerappearing in bothBindProposedevents would tie the private escrows to the handle. - Timing still leaks. Binding both kinds in the same minute lets an observer guess they belong together. Space them out.
Limits you should know about
- Naming is not endorsement. Anyone can launch a coin naming @alice. Until she binds, the token page says she has not claimed or endorsed it.
- The handle-to-id label is attested, not proven. It is an attestor's signature from launch time. The id itself commits to the immutable numeric id, so a later handle change or resale cannot redirect funds.
- Direct exits are refused by the relayer and the UI, not by the contract. An owner who self-submits a direct claim can still do it, and publishes their own wallet by doing so.
- The platform learns nothing extra. Attestors see the user id and the stealth key, but in this mode both are already public. They never see the Railgun address.
- Attestors must be live. Like private escrows, binding needs
attestorThresholdattestors on the FeeVault. Until they are configured, fees can only leave through the fallback.