Build a dapp on Chia
Every DIG primitive is documented on its own — scaffolding, the in-page wallet, the read path, spends, deploy. This page is the single thread that ties them together into one shipped dapp. You'll start from an empty folder and finish with a wallet-aware React app, live on-chain at your own domain.
The whole loop up to publishing is free — scaffold, develop, and preview cost nothing. You pay the uniform capsule price in $DIG only at the deploy step.
new ──▶ dev ──▶ wire wallet (dig-sdk) ──▶ build a spend (chip35) ──▶ deploy ──▶ custom domain
free free free free capsule price free
What you'll need
- The
dig-storeCLI installed. - Node 18+ and
npm. - A funded Chia wallet — only at the deploy step (the uniform capsule price in $DIG + a small XCH fee). Everything before that is free.
1. Scaffold a React app — free, no chain
digs new writes a runnable, wallet-wired project. Pick the React template:
digs new vite-react my-dapp
cd my-dapp
You get a Vite + React app, a dig.toml (output-dir = "dist", build-command = "npm install && npm run build"), and an App.jsx already wired to the in-page wallet. No store is minted and nothing is spent — new is purely local.
npm create dig-appnpm create dig-app@latest my-dapp -- --template vite-react scaffolds the same template straight from npm — the JS front door, no dig-store install needed to start. See Scaffold an app for all five templates and how the two front doors compare.
2. Develop against the real read path — free
digs dev
dev runs your build, serves the output over the genuine chia:// read path (compile → verify → decrypt), and injects a window.chia dev shim so you can build the wallet flow with no real wallet. Edit src/App.jsx, save, and the page live-reloads — exactly what visitors will get, with zero chain interaction and zero spend.
3. Wire the wallet with the SDK — window.chia + WalletConnect fallback
The scaffold talks to window.chia directly, which is perfect inside the DIG Browser. To also support users on other browsers, add the SDK — it prefers the injected window.chia wallet and falls back to WalletConnect → Sage behind one normalized surface, so you write the wallet flow once.
npm i @dignetwork/dig-sdk
npm i @walletconnect/sign-client # optional: only for the WalletConnect fallback
// src/App.jsx
import { useState } from "react";
import { ChiaProvider } from "@dignetwork/dig-sdk";
export default function App() {
const [address, setAddress] = useState(null);
async function login() {
// "auto" prefers the injected DIG Browser wallet, else WalletConnect → Sage.
const provider = await ChiaProvider.connect({
mode: "auto",
walletConnect: {
projectId: import.meta.env.VITE_WC_PROJECT_ID, // a PUBLIC build-time value
metadata: {
name: "My DIG dapp",
description: "Built with @dignetwork/dig-sdk",
url: "https://my-dapp.example",
icons: ["https://my-dapp.example/icon.png"],
},
onUri: (uri) => console.log("Scan to connect:", uri), // render a QR
},
});
setAddress(await provider.getAddress());
}
return (
<main>
<h1>My DIG dapp</h1>
<button onClick={login}>Connect wallet</button>
{address && <p>Connected: {address}</p>}
</main>
);
}
One connect() works in the DIG Browser (no QR, no relay) and everywhere else (WalletConnect). provider.backend tells you which transport connected. The method names and result shapes are identical either way — see Using window.chia for the integration guide, or the normative window.chia provider spec for the exact method/param/return/error contract.
VITE_WC_PROJECT_ID is compiled into your bundle and is world-readable — that's correct for a WalletConnect cloud id. Never put a wallet seed, deploy key, or any secret in the bundle: a capsule is a blind static artifact with no server secrets.
4. Build and sign a spend — the chip35 wasm, via the SDK
When your dapp needs to do something on-chain (mint a store, update metadata, build a CAT payment), it builds the spend with the canonical CHIP-0035 spend builder and hands it to the wallet to sign. The SDK re-exports that builder at the /spend subpath — you never hand-roll a spend bundle.
import { ChiaProvider } from "@dignetwork/dig-sdk";
import * as spend from "@dignetwork/dig-sdk/spend"; // the chip35 wasm builder
async function doSpend() {
spend.init();
// Build coin spends with the wasm builder, e.g. spend.mintStore(...) /
// spend.updateStoreMetadata(...) / spend.buildDigPayment(...). The builder is
// offline and pure — no keys, no network.
const coinSpends = /* spend.mintStore({ ... }) */ [];
// Hand them to the wallet to sign (the wallet holds the keys, not your dapp).
const provider = await ChiaProvider.connect({ mode: "auto" });
const aggregatedSignature = await provider.signCoinSpends(coinSpends);
// → combine into a spend bundle and broadcast.
}
This is the exact pattern the hub uses: build the bundle in-browser with the wasm, sign it with the wallet. The spend builder is the single canonical source of spend bundles across the whole ecosystem, so your dapp produces byte-identical spends to the hub and CLI.
To read verified, encrypted content back (e.g. render another store's data inside your dapp), use the SDK's DigClient:
import { DigClient } from "@dignetwork/dig-sdk";
const dig = new DigClient(); // defaults to https://rpc.dig.net
const html = await dig.readText({
urn: "urn:dig:chia:<storeId>/index.html",
root: "<onchain-root-hex>", // the trust anchor, read from the chain
});
DigClient derives the URN's keys in the browser, verifies inclusion against the on-chain root, and decrypts — the serving host stays blind. See What is the dig RPC?.
PaywallTo monetize — pay-to-unlock content, or gate it on owning an NFT — the SDK ships a high-level Paywall helper that composes a connected ChiaProvider with the spend builder so you don't wire payments by hand: paywall.requestPayment({ amount, owner }) pays the dapp owner and returns a receipt, and paywall.verifyReceipt(...) / paywall.proveAccess({ nft | collection }) gate access.
import { ChiaProvider, Paywall } from "@dignetwork/dig-sdk";
const provider = await ChiaProvider.connect({ mode: "auto" });
const paywall = new Paywall({ provider });
const receipt = await paywall.requestPayment({ amount: 5, owner: "<your-address>" });
if (await paywall.verifyReceipt(receipt)) { /* unlock the content */ }
5. Deploy on-chain
You build and preview for free; this is the only step that spends. First create the store once:
digs init my-dapp --dir dist # mint the store's first capsule (uniform capsule price + XCH fee)
init mints a Chia singleton on mainnet — the launcher id becomes your store id. Copy it into dig.toml (store-id = "<64-hex>"). From then on, one command builds and publishes a new capsule:
digs deploy --json # runs build-command, stages dist/, advances the root
Each deploy publishes a new immutable capsule for the uniform capsule price. The moment it confirms, your dapp is readable over the dig RPC by its URN / chia:// address — encrypted, verified, and impossible to take down, with no registration and nothing more to pay. (A friendly *.on.dig.net web address is a separate, optional step — see the next section.) For push-to-deploy on every commit, wire up Deploy from GitHub Actions.
6. Put it on your own domain
Your store is already reachable by its URN / chia:// address — but for a friendly web URL you register a name. A store gets a *.on.dig.net subdomain when you register a handle for it in DIGHUb: a separate, paid registration that pins the store to that name (no registration → no *.on.dig.net address). To serve it from a domain you own instead, add a custom domain with TLS in DIGHUb ↗ — point your domain at the store and DIGHUb handles the certificate. Either way your dapp loads from a human-friendly URL while staying fully decentralized underneath.
When CHIP-54 .dig handles land, a store will also be addressable by a human-readable .dig name; until then, custom domains via DIGHUb are the way to brand a deployment.
You shipped a dapp
You went from an empty folder to a wallet-aware React app, live on Chia mainnet at your own domain — touching every primitive: scaffolding, the in-page wallet, the SDK, the spend builder, the read path, and deploy. Clone a finished version from the example gallery.
Related
- Scaffold an app (create-dig-app) — the five templates and the npm vs CLI front doors
- Example gallery — clone a finished dapp and open it in a template
- Using window.chia — the in-page wallet provider in full
- The window.chia provider spec — the normative, versioned provider contract
- Project config & build-time values — dig.toml + PUBLIC config
- Deploy from GitHub Actions — push-to-deploy in CI
- What is the dig RPC? — reading verified, encrypted content
- Quickstart — the shorter "ship a site" path
- Concepts & glossary — capsule, store, URN, and window.chia defined