Covenant

Tokenized property where Hedera itself refuses any transfer to an unverified wallet.

Covenant

Created At

ETHGlobal Lisbon 2026

Project Description

Buying property means trusting two things you cannot check: that the property is real and belongs to the seller, and that the counterparty is allowed to be there. The industry's answer to both is a company that verified everyone — and a database of passport scans waiting for a breach.

Covenant answers them with checks that survive us. Two rules define the system.

No token exists until a human reviewer signs an Ed25519 attestation over the property's documents. The signature binds the property id, the seller, the document root and an expiry into one payload, and tokenization accepts nothing else — alter a single character and the mint is refused. The documents themselves never leave the server: only a salted Merkle root and a file count are published, so a seller can later prove a document belongs to the set without revealing the others.

No share reaches a wallet that has not proven eligibility — and Hedera enforces that, not our code. The token carries a KYC key, so a transfer to an account without a grant is refused at consensus with ACCOUNT_KYC_NOT_GRANTED_FOR_TOKEN and an empty transfer list. There is no branch in our codebase that could be deleted to permit it. Buyers prove they are a unique human over 18 through World ID; we receive a nullifier, never an identity.

The same three phases run a second mode. A sale settles as a KYC-gated share transfer with a 2% fee assessed by the token's own schedule. A rental settles as an HBAR escrow release — or, if the landlord lets the window lapse, a refund plus a penalty drawn from the landlord. Same verifier, same World actions, same ENS records; only the predicate and the settlement change.

Everything is live on Hedera testnet: token 0.0.9734808 with a fixed supply of 1000, audit topic 0.0.9734777, and every step of both flows recorded as digests that anyone can read straight from a public Mirror Node endpoint without going through our server.

How it's Made

Next.js 16 with route handlers, and no Solidity anywhere — every on-chain operation is a native Hedera SDK transaction.

Hedera does the enforcement. The property token is created through HTS with a KYC key, a freeze key, a finite supply of 1000, and a 2% fractional custom fee — and deliberately with no supply key and no fee schedule key, so neither the supply nor the fee can be changed after minting. Eligibility becomes a property of the token rather than of our application: an account must be associated (which requires its own signature) and hold a KYC grant, or consensus refuses the transfer. The fee is inclusive and assessed by the network — 100 sent, 2 to the collector, 98 received. Every protocol step submits a message to a Hedera Consensus Service topic carrying event names and digests only, and the read path filters by payer account, because the topic has no submit key and anyone can post a well-formed forgery to it.

World ID 4.0 handles personhood. The server signs a relying-party request with a nonce and a five-minute window before the widget will produce anything, then re-verifies the returned proof against World's backend — the client's success flag is never trusted. We use three separate actions for seller, buyer and tenant, so the same person can hold more than one role without one proof consuming another. The nullifier is never stored raw, only as an HMAC under a server secret; a plain hash would be reversible by anyone who can enumerate the space.

ENS carries the protocol configuration. Each property's subname holds nine text records under a com.pprev namespace — mode, network, token id, audit topic, policy hash, the verifier's public key — written in a single multicall and resolved at runtime through the v2 alpha UniversalResolver on Sepolia. Because the verifier's public key is published there, anyone can check one of our attestations without asking us for anything. And it is not decorative: before any share moves, the token named by ENS is compared with the token about to be transferred, and a record naming a token this protocol did not create refuses the transfer.

Two things worth mentioning. The document commitment is domain-separated and length-prefixed — SHA-256 over a leaf prefix, the property id with its length, the index, a 256-bit salt and the file hash — which closes the second-preimage attack on unprefixed Merkle trees and the field-boundary ambiguity that plain concatenation allows. And on the ENS v2 alpha we found that the parent resolver accepts setText on subnames that were never registered: our wallet's history contains no subname registration at all, yet every name resolves. That gave us an unbounded namespace at zero registration cost, and we wrote it up as feedback.

background image mobile

Join the mailing list

Get the latest news and updates

Covenant | ETHGlobal