An interactive UML / Merise diagram of any Dash Platform data contract. Fetch a contract by id (or paste its JSON) and see its document types as draggable entity tables: fields, indexes, the settings of each type, and the references between them, on a pan/zoom canvas.
- Read-only. Queries are proof-verified via the Evo SDK (trusted mode). No wallet, no writes.
- Protocol version 14 aware. Reads the full Platform 4.2 contract language: declared references (
refersTo), typed arrays,ttl,immutable,propertyConstraints, action fees, moderation, ranked and time-range indexes, index-only types and the rest. See What the diagram shows. - Two notations, toggle any time: UML class diagram and Merise (conceptual) view.
- Static. Vite + React + TS, WASM inlined → deploys to GitHub Pages with no backend.
- Offline-capable. Paste contract JSON or open a bundled example without any network.
npm install
npm run dev # http://localhost:5173Then either:
- open one of the Examples (or visit
?example=marketplace,?demo=1for the default); - pick a network, paste a contract id, and click Load (a devnet also needs its name, e.g.
moutai); - put a link to a contract JSON file in the same box, or open
?url=<link>: handy for a contract in a pull request that is not registered yet. GitHub page links (github.com/<owner>/<repo>/blob/<ref>/<path>) are fetched fromraw.githubusercontent.com; any other host must allow cross-origin reads; or - click Paste JSON to diagram a contract (or a bare document-schemas block) locally, handy while authoring a schema before it is registered.
Since protocol version 14 a contract can declare what an identifier points at, and the platform checks it whenever a document is written. Those declarations are drawn as solid edges, coloured by what they point at, leaving from the exact field that declares them:
| Declaration | Edge |
|---|---|
refersTo on a property |
from that field, to a document type of this contract, another contract's document type, or a platform object (identity, identity key, data contract, token) |
refersTo on a typed array's items |
from the array field, multiplicity 0..∗ / (0,n) |
ownerRefersTo / creatorRefersTo |
from the $ownerId / $creatorId row, labelled «owner» / «creator» |
anyOf / allOf |
one edge per operand, labelled anyOf 1/2, anyOf 2/2, … |
lookup |
labelled via <index> |
listElement |
ends on the list field of the target, labelled ∈ <list> |
deletableDocument |
labelled deletable (checked again on every replace) |
An optional field gives 0..1 / (0,1), a required one 1 / (1,1). Other contracts'
document types get their own node (system contracts are named, e.g. DPNS), with an
Open this contract button in the inspector.
Fields that declare nothing still get the old naming heuristics, drawn dashed and
labelled inferred: an identifier named <x>Id / <x>Ref → the type <x>, a field named
like another type's single-field unique key, or two types indexing the same user field.
System fields ($ownerId, $createdAt) never produce an inferred edge. Treat inferred
edges as hints; hide any edge from the inspector, or a whole kind from the legend.
Every keyword links to its chapter of The Dash Platform Book from the inspector.
- Document type chips under each header:
ttl 30d,no edits,no delete,mods delete ≤1w,owner creates,transferable,for sale,history,logged(transfer / purchase / pricing history),fees(actionFees),token cost,count/Σ x/id ranges,sig critical,enc keys/dec keys. An index-only type has a dashed border and the«index-only type»stereotype. - Fields: required dot,
key/ixmarkers, a coloured→for a declared reference, and chips forfixed(immutable),set once(immutableAllowSetting),transient,v2+(requiredSince),enc(encryptedFor),≠ $ownerId(distinctFrom) andpayload(entryPayload). Nested objects are indented under their parent; typed arrays show asstring[],identifier[]; a$refshows itsschemaDefsname. - Indexes: fields in order plus chips for
contested,count,Σ x,range,top-K count/sum/avg(ranked),window 1d/1h(timeRange),→ $ownerId(terminal),prealloc,skip absent,no nulls. - Property constraints get their own compartment, rendered as formulas:
rewardSplit.leader + rewardSplit.equal + rewardSplit.actions = 100. - Contract metadata (top-left): id, owner, version, timestamps, keywords, description, config, moderation (lists, who moderates, election windows, moderated types),
schemaDefs, and every type's chips.
Select a document type and click GroveDB layout in the inspector to see every tree and
element Drive writes for it, under [64, contract id, 1, <type>]: the documents by id (with
their revisions when the type keeps history) and, for each index, the property and value
trees down to the [0] where the index ends. Each layer shows:
- its key (a fixed key, or
‹…›for one key per document or value; a time window level shows its grid,‹$createdAt window: 1d every 1h›); - its element, coloured by what it totals: a plain tree, a count, sum or count-and-sum tree (provable or not), a ranked indexed tree with its axes, or a value / reference;
- the wrapper a continuation tree gets under a counting or summing value tree
(
NonCounted,NotSummed,NotCountedOrSummed); - the indexes that use it, and conditions: the tree a unique index falls back to when a
value is null,
nullSearchable,skipIfAbsent, preallocated indexes, time window overlap; - a
↗link to that kind of layer in the GroveDB structure viewer, which holds the general description (element flags, Merk shapes, ...).
The layout is computed by Drive's own rules (documentTypeLayout in @dashevo/evo-sdk,
dashpay/platform#5153), held to what Drive
writes by a Drive test, and needs no network. It needs an SDK release that includes it; until
the dependency is bumped the panel says so.
Selecting a document type shows about what creating one of its documents costs, in Dash and dollars; Cost (or details) opens the full breakdown:
- two totals: the first document with a set of index values (it creates their trees) and a later document with the same values (it adds only its own entries);
- the storage, byte for byte: the document itself, and each index on its own, with the part it
shares with other indexes (a common prefix, paid once) and the part only it adds; trees prepaid
for other types' preallocated indexes; a
ttltype's expiration entry; - the processing: the signature and identity fetch (exact) and the writes (estimated for a number of stored documents you can change, with the signing key type and a fee increase);
- what the contract charges: its action fee, a token cost, a contest's vote fund;
- what deleting the document refunds;
- Adjust the document: an on/off switch for each optional field and a length for each variable-size one (strings, byte arrays, arrays). Fixed-size fields (identifiers, numbers, booleans, dates) need no input. By default each variable field sits at the middle of its bounds, the size Drive's own fee estimates assume, and each optional field is present.
The numbers come from Drive (documentCreateCost in @dashevo/evo-sdk), which prices every
element an insert writes with GroveDB's byte formulas and is held to the fees Drive charges by its
tests; see What a Document Costs.
The Dash price comes from CoinGecko (Coinbase if that fails), and you can type your own. Like the
layout, it needs an SDK release that includes it; until then the inspector says so.
Compare in the toolbar diagrams two versions of a contract at once: the union of both,
with added parts in green (+), removed parts struck out in red (−) and changed parts in
amber (~), for document types, properties, indexes, property constraints and reference
edges. Unchanged types are dimmed (or hidden with hide unchanged types).
- Base and head are each a registered contract id (fetched from the toolbar's network),
a link to a contract JSON file,
example:<key>, or empty for none. - From a GitHub pull request: paste the PR link and pick one of its changed JSON files. Base is the file at the merge base, head at the PR head, as in GitHub's Files changed tab. This uses the public GitHub API (60 requests an hour without signing in; a PR takes three or four).
- Deep links:
?pr=https://github.com/<owner>/<repo>/pull/<n>&file=<path>or?base=<source>&head=<source>(plusnetwork/devnetwhen a source is a contract id). A typical update review:?base=<registered contract id>&head=<link to the PR's JSON>&network=testnet.
The Changes panel lists every change as before → after, grouped by document type. Clicking a change centers its type and opens it in the inspector. Each change is judged by the update rule of its keyword in The Dash Platform Book (the On update row of each keyword's table):
- ✕ refused: a contract update with this change would be refused, with the consensus
error the book names (
IncompatibleDocumentTypeSchemaError10246,DocumentTypeUpdateError40212,DataContractInvalidIndexDefinitionUpdateError10217,DataContractInvalidRequiredFieldsUpdateError10276,DataContractConfigUpdateError40002,InvalidDataContractVersionError10212, ...) and a link to the rule; - ? check: allowed unless something the schema text does not settle, such as a new integer
bound that changes how the integer is stored, or a changed
schemaDefsentry that must stay compatible.
These verdicts only matter for an update of the base. Registering the head as a new contract
is not bound by them. They mirror the book, not the validator. The platform's own check is
DataContract::validate_update in rs-dpp, which the wasm SDK does not expose yet. Config
defaults are filled in on both sides, so a fetched contract (which writes every config key)
and a file (which usually leaves defaults out) compare equal.
Bundled, offline, and each one passes the protocol version 14 contract parser as
@dashevo/evo-sdk compiles it (DataContract.fromJSON(json, true, 14)), checked in CI.
The SDK is built without dpp's validation feature, so this does not run the document
meta-schema or the parser checks gated behind that feature:
| Key | What it shows |
|---|---|
marketplace |
An illustrative contract, not registered anywhere, that uses the protocol 14 keywords together |
moderation-charters |
The system contract behind elected moderation: lookups, list elements, property agreements, an anyOf ownerRefersTo, encrypted join requests |
yappr-likes |
Index-only types from the Drive test suite: terminals, preallocated trees, ranked and time-range indexes |
dpns, dashpay, app-connect, keyword-search, withdrawals |
System contracts, as in the platform repo |
dash-qa |
A contract with no declared references, so every edge is inferred |
npm run validate:examples validates them and checks src/model/fixtures/sdk-references.json,
the SDK's own reading of their references, which the unit tests compare the diagram's
parser against (-- --write regenerates it after an SDK bump or a new example).
- Drag entities; pan/zoom; minimap; re-layout (elk auto-layout) button.
- Click any field / index / constraint / entity / edge / external node for details in the inspector.
- JSON: the inspector of a document type, an index or a property ends with its JSON exactly as the contract writes it (an object property with its members), collapsed until opened, with a Copy button. In compare mode it is the head's JSON, or the base's for what the head removed, and says which.
- Legend (bottom) with filters: declared, inferred, platform objects.
- Export the diagram to PNG or SVG.
- Deep links:
?contract=<id>&network=testnet&view=uml,?contract=<id>&network=devnet&devnet=moutai,?url=<link to a contract JSON file>,?example=<key>(and?demo=1), shareable; the URL updates as you load contracts. For example, a contract file in a pull request:https://dashpay.github.io/contract-visualizer/?url=https://github.com/<owner>/<repo>/blob/<commit>/contracts/<file>.json.
The Vite dev server reads ?url as its own asset-import query; a small dev-only plugin in vite.config.ts serves the app for such page requests. Static hosting needs nothing.
@dashevo/evo-sdk is pinned to 4.2.0-beta.4. It reads contracts from networks on
protocol version 14 (4.2 devnets) as well as testnet and mainnet. Earlier 4.x SDKs cannot
decode the version 2 contract config that 4.2 networks serialize. 4.2.0-beta.5 on npm
depends on a @dashevo/wasm-sdk that was never published, so it does not install.
npm run dev # dev server (serves from / locally)
npm run build # typecheck + production build to dist/
npm run preview # serve the production build
npm test # unit tests
npm run typecheck # tsc, no emit
npm run validate:examples # examples through the platform parser; fixture freshness.github/workflows/deploy.yml typechecks, tests, validates
the examples, builds, and publishes dist/ to Pages on push to main. The build base path
is /contract-visualizer/ (the repo name); set VITE_BASE=/ for a custom domain or Vercel.
Optionally set the CONTRACT_ID / NETWORK repository variables to pre-load a contract on
first paint.
One-time: repo settings → Pages → Source: GitHub Actions.
src/
config.ts # contract id / example / link + network + view resolution (URL / localStorage / env)
urlSource.ts # ?url= and pasted links: GitHub blob -> raw, fetch + parse
price.ts # the Dash price in dollars (CoinGecko, then Coinbase) and a typed-in override
github.ts # a PR's changed JSON files at merge base and head (GitHub REST API)
sdk/
client.ts # Evo SDK trusted connection, memoised per network
contract.ts # any source (id, link, example) -> contract JSON -> ContractModel
local.ts # the SDK functions that run from a contract's JSON alone
layout.ts # documentTypeLayout through the SDK (feature-detected)
cost.ts # documentCreateCost through the SDK (feature-detected)
pool.ts # pooled connections, resettable without loading the SDK
examples/ # bundled example contracts + registry
model/
introspect.ts # document schemas -> ContractModel (fields, indexes, keywords)
references.ts # refersTo / ownerRefersTo / creatorRefersTo -> declared edges
relationships.ts # declared + inferred relationships
constraints.ts # propertyConstraints -> formulas
describe.ts # keyword chips and plain-language descriptions
diff.ts # compare two versions: changes, statuses, the union model
layout.ts # a document type's GroveDB layout (from the SDK) and its display helpers
cost.ts # what a document costs (from the SDK) and its formatting helpers
jsonTokens.ts # JSON syntax highlighting tokens for the inspector
updateRules.ts # each keyword's update rule, per the book, with its error code
types.ts
fixtures/ # the SDK's reading of the examples' references
flow/
diagramToFlow.ts # model + view + filters -> React Flow nodes/edges
layout.ts # elk layered auto-layout
EntityNode.tsx # document type card (UML / Merise rendering)
ExternalNode.tsx # identity / key / contract / token / other contract's type
Canvas.tsx # React Flow canvas, legend, export/re-layout panel
selection.ts, exportImage.ts
components/ # Toolbar, InspectorPanel, ContractMetaPanel, PasteContractModal,
# CompareModal, ChangesPanel, LayoutPanel, CostPanel, CostLine
App.tsx, main.tsx, styles.css
scripts/
validate-examples.mjs # examples through DataContract.fromJSON(…, true, 14)
MIT