Search projects, hackathons, sponsors and tech

    ← All projects

    Obec & dAppSwarm

    ENS as the state machine for neighborhood DAOs

    About

    The problem it solves ## The problem Neighborhoods are full of latent capital nobody coordinates. Fourteen households could share two cargo bikes instead of owning fourteen private cars, but the coordination overhead kills it. Kickstarter doesn't know who lives on your block, co-ops don't ship with L2 escrow. Dappnode has a parallel gap: package distribution still flows through IPFS and a centralised registry. ## What Obec does Obec gives every neighborhood a self-sovereign DAO whose state lives **in ENS itself**. A neighborhood is a subname (e.g. vinohrady.prague.obec.eth), members are sub-subnames, projects are sub-sub-subnames, and the contracts atomically write text records as the funding lifecycle progresses. 1. A member proposes a project. The pool registers an ENS subname with status "proposing". 2. Members commit USDC. When total crosses target and member count crosses the minimum, status flips to "active" and milestone 0 (30%) releases. 3. After delivery, M-of-N attest on-chain; the attesters list anchors as a credential and milestone 1 (50%) releases. 4. After a warranty window, milestone 2 (20%) releases, status flips to "completed". 5. If it never funds, expiry deactivates the row but tombstones the namehash, so the label cannot be reused. addr(node) returns the pool; addr(node, 2147568180) returns the same pool on Base Sepolia; text(node, "status") is the live state; contenthash(node) is the IPFS doc root. ## What dAppSwarm does dAppSwarm is the Rust CLI half: it packs a Dappnode package into a Mantaray manifest on Swarm and stamps each release into a Swarm Feed indexed by package name. Resolvers find the latest with one feed lookup and bring the compose project up — no central registry, no IPFS hop. The publisher's secp256k1 EOA is the trust anchor. ## Why together Obec is the capital layer; dAppSwarm is the distribution layer. A neighborhood that funds a self-hosted service ships it to household Dappnodes through the same trust-rooted, peer-to-peer stack.

    How it's made

    Challenges we ran into ## Single-subname lifecycle The first Obec design used two ENS subnames per project — one for proposal, one for the funded resource. The pre-funding name stopped existing the moment the project funded. We refactored to **one project subname** that carries the full lifecycle: status flips proposing -> active -> completed, and on expiry the registry row deactivates *and* the namehash tombstones in Status.Expired. Pinned by test_checkExpiry_namehashRemainsTombstoned. ## CCIP-Read end-to-end ENSIP-10 wildcard resolver on Sepolia plus an EIP-3668 signed-response gateway as a Next.js route. The resolver reverts with OffchainLookup; the gateway reads Base Sepolia and signs with EIP-191; resolveWithProof verifies. Sepolia lookup, off-chain fetch, signature check, Base Sepolia state read — verifiable from any standard ENS client. ## Cross-chain split ENS on Sepolia; USDC and the pool on Base Sepolia. Resolver, gateway, and ENSIP-9 coin-type space all have to agree, and a domain-separator binding stops a forged gateway from spoofing answers for a different resolver. Wiring wagmi's chain-switching, viem's CCIP-Read client, and the resolver's per-call expiry was most of the integration work. ## SOC pushes without a Bee dAppSwarm writes Single-Owner-Chunk feed entries, but stock antd exposed no POST /soc/{owner}/{id}. We patched antd to accept SOC envelopes and wrote a signer that recovers the owner from the chunk and matches it against the URL before acceptance. ## Feed indexing without a directory service Swarm Feeds give a (topic, owner) -> chunk mapping, not name -> latest version. Topic is keccak256 of "dappswarm:" concatenated with the package name; entries are written at sequential indices 0, 1, 2, and so on; lookup is exponential probe + binary search, O(log n). The bundle's file list lives in the feed payload, so resolution needs no manifest-listing endpoint.