Skip to content

dApp builder API

All under /api/v1/dapp. Route file: bz-backend/v1/routes/dapp.routes.js — roughly 24 endpoints.

Product page: dApp builder. Full design: defi-set/DAPP_BUILDER_ARCHITECTURE.md.

Projects

MethodPathPurpose
GET/v1/dapp/listAvailable templates or dApp list
GET/v1/dapp/projectsThe user's projects
GET/v1/dapp/project/:projectIdOne project
GET/v1/dapp/project/:projectId/htmlRaw HTML for the preview iframe
GET/v1/dapp/project/:projectId/versionsVersion history
POST/v1/dapp/project/:projectId/iterateApply a change
POST/v1/dapp/project/:projectId/rollbackRestore a previous version
DELETE/v1/dapp/project/:projectIdDelete

Legacy aliases also exist: GET /v1/dapp/:dappId and POST /v1/dapp/:dappId/iterate.

The catch-all /:dappId is registered last, and that matters

GET /v1/dapp/:dappId sits after /list, /subscription, /projects and the /project/* and /account/* groups. A new single-segment GET route added below it would be shadowed.

Iteration

POST /v1/dapp/project/:projectId/iterate runs v1/services/aiDeveloper.service.js:

StepPurpose
Complexity checkReject a request bundling more than ~3 distinct changes
Load current versionIterations modify that user's code, not a template
Classify the changevisual / feature / content / contract — picks the model tier and prompt
Call the modelWhole current file as context
Validate outputHTML must parse, scripts intact, no XSS introduced
Save as a new versioncurrentVersionId advances; prior versions retained

Validation is the load-bearing step

An LLM asked to change a colour can silently drop a <script> block. Without the check the user's app quietly breaks and the only signal is the preview no longer working.

Why the complexity ceiling exists

"Change the colour, add staking, wire the contract, and add a leaderboard" asks the model to do four things in one pass, and the failure mode is a partially-applied change that looks complete. Rejecting is better than half-applying.

Going on-chain

ERC-4337 via ZeroDev (@zerodev/sdk, @zerodev/ecdsa-validator, @zerodev/permissions, permissionless).

MethodPathStep
POST/v1/dapp/project/:projectId/go-onchain1. Start setup; quote the ~$10 cost
POST/v1/dapp/account/verify-deposit2. Confirm the deposit landed at the counterfactual address
POST/v1/dapp/account/:projectId/deploy-account3. Deploy the smart account (first UserOp)
GET/v1/dapp/account/:projectIdAccount state
POST/v1/dapp/account/:projectId/top-upAdd paymaster budget
POST/v1/dapp/account/:projectId/revoke-sessionRevoke Blazpay's session key
POST/v1/dapp/account/:projectId/verify-contractSubmit for explorer verification
GET/v1/dapp/account/:projectId/verify-contract/:guidPoll verification status

The sequence

  1. Bot quotes the cost (~$10, covering deployment plus roughly 500 user interactions) and states that the user owns the account via their MetaMask.
  2. User signs a one-time EIP-712 setup message.
  3. Backend computes the counterfactual smart-account address and asks the user to fund it.
  4. Deposit verified on-chain.
  5. Smart account deployed as the first UserOp.
  6. A session key with tight ZeroDev permissions is registered for Blazpay's automation.
  7. Paymaster budget topped up with the user's deposit.
  8. aiDeveloper.service generates Solidity; contractDeployer.service compiles with solc and deploys via the session key plus paymaster.

This is the only Blazpay-held key that can move user funds

The session key is scoped by ZeroDev permissions and revocable unilaterally by the user, after which the smart account is entirely self-controlled and Blazpay's automation stops. It is a genuinely different trust posture from the rest of the platform, and the UI should say so at setup — not in a footer.

Withdrawal

Not an endpoint the backend initiates. The frontend has the user sign { action: 'WITHDRAW', amount, to } with their owner key; the backend verifies and constructs the UserOp.

Subscription and quota

MethodPathPurpose
GET/v1/dapp/subscriptionCurrent tier
GET/v1/dapp/subscription/statusUsage against the tier
POST/v1/dapp/subscription/upgradeUpgrade

Iteration counts are per projectiterationsUsed / maxIterations on models/dappProjectModel.js. Tier data in models/dappSubscriptionModel.js.

Data model

CollectionContents
dappProjectModelProject, version history, currentVersionId, iterationsUsed, maxIterations
dappSubscriptionModelTier and billing
generatedDappModelGenerated artefacts
dappModelTemplates or legacy records
managedAccountModelZeroDev smart accounts
caSca, caActivitySmart-contract-account records and activity (/api/defi/save-sca, /get-sca/:address)

Session state

The active project is activeDappProjectId on blazSession, sent to bz-agent with every chat request. That is what makes MODIFY_DAPP, SHOW_DAPP, GO_ONCHAIN and DEPLOY_CONTRACTS selectable at all — without one, the classifier cannot choose them.

Intents

IntentPreconditionAction
CREATE_DAPPcreate_dapp
MODIFY_DAPPActive projectcreate_dapp
SHOW_DAPPActive projectcreate_dapp
GO_ONCHAINActive project
DEPLOY_CONTRACTSActive project and a smart account

GO_ONCHAIN versus DEPLOY_CONTRACTS

GO_ONCHAIN is first-time smart-account setup. DEPLOY_CONTRACTS is deploying Solidity. Both intent descriptions carry explicit do-NOT-use-for lists because the classifier used to conflate them.

Agent nodes: bz-agent/src/agent/{createDappNode,modifyDappNode,goOnchainNode}.ts. Templates: src/template/{createDapp,modifyDapp,goOnchain}.ts.

Frontend

defi-dex/src/component/defi/swapAI/dapp/DappPreview.tsx — renders the generated HTML in a blob-URL iframe, shows the change summary below the preview, and drives iterate, rollback and go-onchain.

Failure modes

SymptomCause
Iteration rejectedComplexity check — more than ~3 distinct changes
Iteration applied but the app brokeOutput validation should have caught it; check the validator
Changes appear then vanishThe original bug — iterations were hitting a shared template rather than the user's code. Should not recur
MODIFY_DAPP not classifiedNo activeDappProjectId on the session
Deploy failsDeposit not verified, or paymaster budget exhausted
Automation stopped workingThe user revoked the session key
Contract verification pending foreverPoll GET /verify-contract/:guid; explorer-side delay

Known limits

  • Single-file HTML output, rendered in a blob-URL iframe. Not multi-file React.
  • Generated Solidity is not audited. It is compiled and deployed. Routing it through the contract auditor — available in the same chat — is the obvious integration and is not automatic.
  • No export. A user cannot download their codebase and self-host.
  • Chain coverage is narrower than the trading products. See §12 of the architecture document.

Internal + developer documentation. Usage figures are point-in-time snapshots — re-verify before publishing them.