Appearance
dApp builder
Describe a web3 app in chat, get a working preview, iterate by talking to it, then deploy real contracts on-chain through a managed smart account you own.
Intents: CREATE_DAPP, MODIFY_DAPP, SHOW_DAPP, GO_ONCHAIN, DEPLOY_CONTRACTS · Action: create_dapp
Full design document: defi-set/DAPP_BUILDER_ARCHITECTURE.md. Progress log: DAPP_BUILDER_PROGRESS.md.
What it does
Lovable or Bolt, but web3-native. The difference from a generic code generator is that a Blazpay dApp can go on-chain — the builder generates Solidity, deploys it, and wires the frontend to the deployed address, all from chat.
Core design principles
These are from the architecture document and they explain most of the implementation choices:
- Template + AI hybrid. v1 is a fast template render. Every subsequent version is AI-modified actual code, per user.
- Per-user codebase. Each dApp is its own versioned codebase in MongoDB. Iterations modify that user's code, not a shared template. This was the fix for the original iteration bug, where changes appeared to apply and then vanished.
- Non-custodial on-chain. The user owns their smart account through their own MetaMask. Blazpay holds a restricted session key for automation only. Funds stay under user control.
- Paymaster-sponsored gas. The user deposits ~$10 once; the ZeroDev paymaster pays gas invisibly per transaction.
- Cost-controlled AI. A complexity classifier routes simple changes to cheap models; only complex changes reach the expensive tier.
Iteration mechanics
aiDeveloper.service does more than call an LLM:
| Step | Purpose |
|---|---|
| Complexity check | Rejects a request bundling more than ~3 distinct changes, so the model is not asked to do too much at once |
| Load current version | Iterations are against real code, not the template |
| Classify the change | visual / feature / content / contract — routes to the right model tier and prompt |
| Call the model with code + request | The whole current file is context |
| Validate output | HTML must parse, scripts must be intact, no XSS introduced |
| Save as a new version | currentVersionId advances; previous versions remain |
Validation is the load-bearing step. An LLM asked to change a colour can silently drop a <script> block, and without the check the user's app quietly breaks.
Versioning and rollback
| Endpoint | Purpose |
|---|---|
GET /v1/dapp/project/:projectId/versions | Version list |
POST /v1/dapp/project/:projectId/rollback | Restore a previous version |
GET /v1/dapp/project/:projectId/html | Raw HTML for the preview iframe |
POST /v1/dapp/project/:projectId/iterate | Apply a change |
DELETE /v1/dapp/project/:projectId | Delete |
Going on-chain
ERC-4337 via ZeroDev (@zerodev/sdk, @zerodev/ecdsa-validator, @zerodev/permissions, plus permissionless).
The sequence, from the architecture document:
- The user asks to make the app real. The bot quotes the cost (~$10, covering contract deployment plus roughly 500 user interactions) and states plainly that the user owns the account via their MetaMask.
- The user signs a one-time EIP-712 setup message.
smartAccount.servicecomputes the counterfactual smart-account address, asks the user to send $10 of native to it, and verifies the deposit on-chain.- The smart account is deployed as the first UserOp.
- A session key with tight permissions is registered for Blazpay's automation.
- The ZeroDev paymaster budget is topped up with the user's deposit.
aiDeveloper.servicegenerates the Solidity.contractDeployer.servicecompiles withsolcand deploys via the session key plus paymaster.
Endpoints
| Endpoint | Purpose |
|---|---|
POST /v1/dapp/project/:projectId/go-onchain | Start setup |
POST /v1/dapp/account/verify-deposit | Confirm the $10 landed |
POST /v1/dapp/account/:projectId/deploy-account | Deploy the smart account |
GET /v1/dapp/account/:projectId | Account state |
POST /v1/dapp/account/:projectId/top-up | Add paymaster budget |
POST /v1/dapp/account/:projectId/revoke-session | Revoke Blazpay's session key |
POST /v1/dapp/account/:projectId/verify-contract | Submit for explorer verification |
GET /v1/dapp/account/:projectId/verify-contract/:guid | Poll verification status |
Withdrawal and revocation
Both are user-controlled:
- Withdraw — the frontend has the user sign
{ action: 'WITHDRAW', amount, to }with their owner key. The backend verifies the signature and constructs the UserOp. - Revoke — the user can revoke the session key at any time, after which the smart account is entirely self-controlled and Blazpay's automation stops working.
This is the only place Blazpay holds a key that can move user funds
The session key is scoped by ZeroDev permissions to the operations the builder needs, and revocable unilaterally by the user. It is a genuinely different trust posture from the rest of the platform, and the UI should say so at the point of setup — not in a footer.
Subscription and quota
| Endpoint | Purpose |
|---|---|
GET /v1/dapp/subscription | Current tier |
GET /v1/dapp/subscription/status | Usage against the tier |
POST /v1/dapp/subscription/upgrade | Upgrade |
Iteration counts are per-project — iterationsUsed / maxIterations on the project record, in models/dappProjectModel.js. Tier data lives in models/dappSubscriptionModel.js.
Session state
The active project is activeDappProjectId on blazSession, passed to bz-agent with every chat request. That is how the precondition on MODIFY_DAPP, SHOW_DAPP, GO_ONCHAIN, DEPLOY_CONTRACTS and SHOW_DAPP is enforced — without an active project, those intents are not selectable.
Under the hood
| Concern | Where |
|---|---|
| Project + version storage | bz-backend/models/dappProjectModel.js, generatedDappModel.js, dappModel.js |
| Subscription | models/dappSubscriptionModel.js |
| Smart account record | models/managedAccountModel.js |
| Generation + iteration | v1/services/dappBuilder.service.js, v1/services/aiDeveloper.service.js |
| Routes | v1/routes/dapp.routes.js (~24 endpoints) |
| Agent nodes | bz-agent/src/agent/{createDappNode,modifyDappNode,goOnchainNode}.ts |
| Templates | bz-agent/src/template/{createDapp,modifyDapp,goOnchain}.ts |
| Chat card | defi-dex/src/component/defi/swapAI/dapp/DappPreview.tsx |
Endpoint detail: dApp API reference.
Intent boundaries
GO_ONCHAIN versus DEPLOY_CONTRACTS is the confusion to watch, and both descriptions carry explicit exclusion lists:
| User says | Intent |
|---|---|
| "go onchain", "set up smart account", "make my dapp live" | GO_ONCHAIN |
| "deploy my contracts", "deploy solidity", "deploy via smart account", "generate contracts" | DEPLOY_CONTRACTS |
| "show my dapp", "open preview", "let me see it" | SHOW_DAPP |
Known limits
- HTML single-file output. Generated dApps are self-contained HTML rendered in a blob-URL iframe, not multi-file React projects. That bounds what can be built.
- Complexity ceiling per iteration. More than ~3 distinct changes in one message is rejected rather than attempted.
- Generated Solidity is not audited. It is compiled and deployed. Users deploying a generated contract that will hold funds should run it through the contract auditor — which is available in the same chat, and is the obvious integration to make automatic.
- Chain coverage is narrower than the trading products. See §12 of the architecture document for the target list.
- The session key is a real trust delegation. Documented above; make sure the UI is honest about it.
- No export. A user cannot download their generated codebase and self-host it. That is a retention decision, but it is also a lock-in users will eventually object to.