Search projects, hackathons, sponsors and tech

    ← All projects

    D3ploy

    A censorship-resistant deployment platform

    About

    The problem it solves ## The Problem Every DeFi frontend today has a dirty secret: **it's centralized**. The smart contracts are on-chain. The tokens are yours. But the website you use to interact with them? It runs on AWS, Vercel, or Cloudflare servers controlled by a company that can be hacked, subpoenaed, or simply decide to pull the plug. This has already caused real damage: - **Curve Finance, Uniswap, Balancer** frontends hijacked via DNS attacks to drain user wallets - **Tornado Cash** OFAC pressured the frontend offline within hours - **Compound, dYdX** geo-blocked entire regions via their hosting provider - **Hundreds of smaller protocols** taken offline the moment their Vercel free tier expired or their domain registrar received a complaint The smart contract is trustless. The frontend is not. **That gap kills people.** --- ## What Deploy.eth Solves Deploy.eth makes your entire stack trustless — not just the contracts. ### 🌐 For DeFi Protocol Teams **Ship a frontend that cannot be taken down.** ```bash web3deploy push # ✓ Site live on IPFS (content-addressed, immutable) # ✓ myprotocol.eth now resolves to your new build # ✓ No Vercel. No AWS. No domain registrar. No kill switch. ``` Your site lives on IPFS a global peer-to-peer network where files are addressed by their content hash, not a server location. Nobody can redirect it, modify it silently, or take it offline. Even if your entire team disappears, the frontend keeps serving users. **Store your protocol config on-chain, not in a dashboard.** Every deploy writes your configuration to ENS text records publicly auditable, immutable history, readable by your smart contracts: ``` swap.slippage → 0.5 fee.recipient → 0x... fee.bps → 30 access.policy → public monitor.riskLevel → medium ``` Users can verify exactly what your frontend is configured to do before they connect their wallet. No more "trust us." **Require multi-sig approval before any frontend update goes live.** Malicious frontend updates are one of DeFi's biggest attack vectors. Deploy.eth lets you require 3-of-5 team signatures before the ENS contenthash changes making a frontend attack as hard as a smart contract exploit. --- ### 📊 For DeFi Users **A live, intelligent frontend powered by Elsa x402 with no backend.** Because there's no server, there are no API keys to steal, no database to breach, and no company to subpoena for your transaction history. Every data call is a direct pay-per-use request: | What you get | How | Cost | |---|---|---| | Portfolio across 15+ chains | `elsa_get_portfolio` | $0.01 | | Live yield suggestions | `elsa_get_yield_suggestions` | $0.02 | | Best swap quote across 20+ DEXs | `elsa_get_swap_quote` | $0.01 | | P&L report | `elsa_get_pnl_report` | $0.015 | Your ENS domain becomes your **personal DeFi config** — store your risk tolerance, preferred chains, slippage limits, and the site respects them automatically, every time, on any device, without logging in. **Your site watches your portfolio even when you're not looking.** ENS monitor records tell the site what to watch for: ``` monitor.alertThreshold → 0.15 (alert if 15% drawdown) monitor.autoRebalance → true monitor.preferredYield → 0.08 (suggest moves if APY < 8%) ``` When conditions are met, your ungoverned frontend surfaces the action. You confirm. Elsa executes. No email. No notification from a company that knows your wallet address. --- ### 🔒 For DAOs and Governance-Heavy Protocols **Make frontend deployments a governance action.** Right now, a DAO can vote on treasury decisions, parameter changes, and contract upgrades — but a single developer can push a malicious frontend unilaterally. Deploy.eth closes that gap. Every frontend update is a proposal. Signers review the CID diff. The ENS contenthash only changes when the threshold is met. The full deploy history — who proposed what, when, with which CID — lives on-chain forever. **Environment routing without a backend.** ``` myprotocol.eth → production (CID: bafybeig3...) staging.myprotocol.eth → staging (CID: bafybeia7...) v2.myprotocol.eth → beta (CID: bafybeif2...) ``` Each subname is independently owned. Staging can move fast. Production requires governance. All of it lives in ENS — no DevOps, no infra bill. --- ### 🛠 For Developers **One command. Everything decentralized.** ```bash npx web3deploy init # scaffold config web3deploy push # build → IPFS → ENS → done web3deploy rollback # one command back to any previous deploy web3deploy env set swap.slippage 0.3 # update on-chain config ``` No accounts to create. No credit card. No vendor lock-in. If Deploy.eth shuts down tomorrow, your site keeps running — the files are on IPFS, the domain is in ENS, the history is on-chain. The platform is optional infrastructure, not a dependency. --- ## The Core Insight > A truly trustless DeFi app needs every layer to be trustless > not just

    How it's made

    Challenges we ran into ## Challenges I Ran Into ### 1. ENS `contenthash` encoding is not what you think it is The first wall I hit. ENS doesn't store a raw IPFS CID string in the `contenthash` field — it stores a binary-encoded value following a specific multicodec spec. Passing a raw CIDv1 string to the resolver contract just reverts silently. The CID has to be encoded as: ``` 0xe3010170 + varint(cid-length) + cid-bytes ``` The `0xe3010170` prefix encodes the namespace (IPFS), the version, and the codec. Miss any byte and the ENS gateway refuses to resolve it — with no error message, just a blank page. **Fix:** Used `@ensdomains/content-hash` library which handles the encoding correctly. But it took hours to diagnose because the ENS resolver accepts the malformed transaction — it only fails silently at resolution time, not at write time. ```typescript import { encode } from '@ensdomains/content-hash'; const encoded = encode('ipfs', cid); // '0xe301017012...' ``` --- ### 2. IPFS CIDs change every build even when nothing changes The second major hurdle. Vite's build output includes chunk hashes in filenames — `index-BvXk3a91.js` — so every build produces a different `dist/` tree even if the source hasn't changed. Every different tree produces a different root CID. This meant every `git push`, even a README typo fix, produced a new CID, a new ENS transaction, and ~$5-15 in gas fees on mainnet. **Fix 1:** Switched to deterministic chunk naming in `vite.config.ts`: ```typescript build: { rollupOptions: { output: { entryFileNames: 'assets/[name].js', chunkFileNames: 'assets/[name].js', assetFileNames: 'assets/[name].[ext]', } } } ``` **Fix 2:** Added a CID comparison step in the deploy script — reads the current ENS `contenthash`, decodes it, compares with the new CID, and skips the ENS transaction entirely if they match: ```typescript const currentCid = await getCurrentEnsCid(domain); if (currentCid === newCid) { console.log('CID unchanged, skipping ENS update'); process.exit(0); } ``` Gas saved. Sanity restored. --- ### 3. Elsa x402 payment tokens expire mid-pipeline The x402 payment flow generates a short-lived JWT-style payment token before each API call. In a multi-step pipeline — `get_portfolio` → `get_yield_suggestions` → `execute_swap_dry_run` — if any step takes longer than expected (slow IPFS pin, RPC congestion), the token for the next step has already expired by the time you use it. This caused the swap dry-run to fail silently, returning a `402 Payment Required` response that I initially mistook for a broken endpoint. **Fix:** Generate payment tokens lazily — immediately before each individual API call rather than upfront for the whole pipeline: ```typescript // ❌ Wrong — tokens generated upfront, may expire const tokens = await generateTokens(['portfolio', 'yield', 'swap']); await getPortfolio(tokens[0]); await sleep(3000); // tokens[1] may already be stale await getYield(tokens[1]); // ✅ Correct — token generated fresh per call await getPortfolio(await generateToken('portfolio')); await getYield(await generateToken('yield')); await executeDryRun(await generateToken('swap')); ``` --- ### 4. ENS text record updates require a separate resolver transaction I assumed updating the `contenthash` and updating text records could be batched into one transaction. They can't — at least not on the standard public resolver. Each `setText` call is a separate transaction, and with 8 text records to update per deploy (CID, timestamp, framework, env, tx hash, version, build command, node version), the deploy script was firing 9 transactions sequentially. At ~15 seconds per transaction confirmation and gas for each, a full deploy was taking 3+ minutes and costing more in gas than the hosting was worth. **Fix:** Used ENS's `multicall` on the resolver contract, which batches multiple `setText` and `setContenthash` calls into a single transaction: ```typescript const resolver = await provider.getResolver(domain); await resolver.multicall([ resolver.interface.encodeFunctionData('setContenthash', [node, encoded]), resolver.interface.encodeFunctionData('setText', [node, 'deploy.cid', cid]), resolver.interface.encodeFunctionData('setText', [node, 'deploy.timestamp', ts]), // ... all 8 records ]); ``` One transaction. One gas fee. Deploy time dropped from 3 minutes to under 30 seconds. --- ### 5. IPFS gateway resolution lag breaks the "instant deploy" experience After updating the ENS contenthash to a new CID, there's a propagation delay before IPFS gateways (ipfs.io, cloudflare-ipfs.com, eth.limo) actually serve the new content. The CID is correct in ENS immediately, but visiting `myapp.eth.limo` still shows the old version for anywhere from 30 seconds to 5 minutes. Users opening the site right after a deploy would see the old version with no indication that a new one exists — confusing and undermining trust in the platform. **Fix:** Two-part. First, added a deploy banner comp