Skip to content

Repository files navigation

Dash Contract Visualizer

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.

Quick start

npm install
npm run dev        # http://localhost:5173

Then either:

  • open one of the Examples (or visit ?example=marketplace, ?demo=1 for 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 from raw.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.

How relationships work

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.

What the diagram shows

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 / ix markers, a coloured → for a declared reference, and chips for fixed (immutable), set once (immutableAllowSetting), transient, v2+ (requiredSince), enc (encryptedFor), ≠ $ownerId (distinctFrom) and payload (entryPayload). Nested objects are indented under their parent; typed arrays show as string[], identifier[]; a $ref shows its schemaDefs name.
  • 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.

How a document type is stored (GroveDB layout)

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.

What a document costs

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 ttl type'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 two versions (contract updates)

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> (plus network / devnet when 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 (IncompatibleDocumentTypeSchemaError 10246, DocumentTypeUpdateError 40212, DataContractInvalidIndexDefinitionUpdateError 10217, DataContractInvalidRequiredFieldsUpdateError 10276, DataContractConfigUpdateError 40002, InvalidDataContractVersionError 10212, ...) 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 schemaDefs entry 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.

Examples

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).

Interaction, export, deep links

  • 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.

SDK version

@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.

Scripts

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

Deployment (GitHub Pages)

.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.

Project structure

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)

License

MIT

About

Interactive UML / Merise diagrams of Dash Platform data contracts (Vite + React + TS, Evo SDK, React Flow)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages