ACME
Back to Insights
The Canton dApp Stack: How You Read Data and Connect Wallets
Blog

The Canton dApp Stack: How You Read Data and Connect Wallets

A practical comparison of how Canton dApps differ from EVM dApps, covering party-scoped data, Ledger API reads, private indexing through PQS, wallet connection standards, and what EVM builders need to redesign when building on Canton.

May 22, 2026 at 12:00 AM6 min readCanton Network
Nick Sawinyh
Nick Sawinyh
Founder IRSForge

Point a Solidity team at a new EVM chain and the read path is muscle memory. Get an RPC key from Alchemy or Infura. Deploy a subgraph, or skip it and use Envio. Wire the frontend with wagmi and viem. Drop in a WalletConnect modal. None of that is a design decision anymore. It is the default stack, standardized so thoroughly that nobody on the team thinks about it.

Take that same team to Canton and every one of those pieces is shaped differently. There is no Alchemy to point at. There is no subgraph. The wallet modal is new enough that it shipped this spring. And the reason all of those pieces changed is the same reason, stated once: Canton has no global replicated state. Data lives in partitioned, party-scoped sub-ledgers behind participant nodes, not in a world state that every node mirrors. That single architectural fact reorganizes how a frontend reads data and how a wallet connects an identity.

Most developer introductions to Canton focus on the contract layer, the mental-model inversions a Solidity developer hits in the Daml code itself. This post covers everything around the contracts: the plumbing. Where the data is, how your app reads it, why there is no indexer to point at, and how a wallet attaches a user to your dApp. It is about wiring the application, not writing the contract.

The shape of a Canton dApp

Start with where data physically sits, because everything else follows from it.

On an EVM chain, a smart contract is an object in a single global state trie. Every full node maintains a copy of (close to) the entire state. Your frontend reaches that state through an RPC node, usually a hosted one, or more often through an indexer that has pre-digested the chain's public event logs into something queryable. Identity is an address: the hash of a key pair, valid on every node, owned by whoever holds the key.

On Canton, there is no global trie. The unit of participation is a Party, and a Party is hosted on a participant node. A participant node stores only the contracts that its parties are a party to, whether as signatory, observer, or otherwise entitled to see. Two participant nodes running on the same network can hold completely disjoint sets of contracts. A synchronizer (older docs say "domain"; treat them as the same thing) orders transactions and guarantees atomic settlement across those private ledgers without forcing global visibility. The Canton technical primer is the canonical description.


The practical consequence for an application developer:


That last row is the one most EVM teams underestimate. On Canton you will almost certainly run an app backend, and the frontend will almost certainly talk to that backend rather than to the ledger directly. More on why below.

Reading data: the Ledger API

A Canton dApp reads and writes through the Ledger API, exposed by participant nodes. There is no public "world state" endpoint because there is no world state. You query the node that holds the data your party is allowed to see.

The Ledger API comes in two transports. The gRPC Ledger API is the full-power surface, used by backends. The JSON Ledger API (the v2 line, which replaced the deprecated v1) is the HTTP/JSON surface, easier to reach from languages and runtimes where gRPC is awkward. Digital Asset's Ledger API documentation is the reference.

You do three kinds of thing with it. You query the Active Contract Set, the ACS, the set of contracts currently live (created, not yet archived) that a given party can see, filtered by template and by party. This is the closest analogue to "read state," except it is already a structured query against typed records rather than a scan: you ask the node, acting as your party, for every live Holding contract, and it returns the ones your party is a stakeholder on. The filter works on template and requesting party. Predicate queries on a field value are a job for PQS, below. You submit commands, exercising a choice on a contract, after which the protocol checks authorization and either commits the transaction or rejects it. And you subscribe to the transaction stream for your parties, reacting to contract creations and archives as they land. That last one is how a Canton app gets real-time updates: a direct subscription on the node, not a poll against an indexer.

One thing the Ledger API does not give you is custom events. Daml contracts do not emit anything. State changes are observable as transaction trees, and that is the whole story. This is a common migration friction point: the off-chain plumbing an EVM team built reflexively around The Graph does not transfer. Here is the architecture underneath that friction, and why it is not a gap so much as a different shape.

Why the backend. The common production topology is frontend → app backend → participant node. The Canton Network Quickstart ships exactly this: a TypeScript frontend, a Java/Spring backend, the backend holding the Ledger API connection. This is a recommendation, not a hard rule, but the reasons it is the default are worth stating. The Ledger API is authenticated (JWT-based in the institutional path); it is not designed as a public read endpoint you expose to a browser. Putting a backend in front gives you a place to hold credentials, enforce app-level logic, and keep a non-repudiation boundary that institutional deployments care about. An EVM team used to a backendless dApp, frontend straight to chain, should plan for that backend as a real component, not an afterthought.

Why there is no The Graph on Canton

On EVM, indexing became an industry. The Graph, Envio, Subsquid, Goldsky. A whole category exists to turn raw chain logs into fast, relational, GraphQL-shaped data. That category exists because three things are true at once on a public chain: the data is globally public, raw RPC is slow for historical and relational queries, and every dApp needs roughly the same indexing work. When the same plumbing is needed by everyone and the underlying data is open to everyone, it gets built once and sold as shared infrastructure.

On Canton, none of those three conditions holds the same way, so a network-wide public indexer does not make sense:

  1. Privacy makes a public indexer impossible, not just unnecessary. Most data on Canton is private, visible only to the parties of each contract. A public indexing service cannot index what it cannot see. There is no global public dataset to point a crawler at. The Graph's business model has nothing to consume.
  2. The data is already partitioned and scoped. Your application's contracts live on the participant nodes of your application's parties. You are not searching a global chain for the slice you care about. You are already holding only that slice. Querying the ACS by template and party is itself the structured query an EVM team would have built an indexer to provide.
  3. Typed records, not a log firehose. Daml contracts are typed records with explicit parties and fields. The ACS is closer to a queryable table than to an undifferentiated stream of event logs. There is less raw-to-useful transformation to do in the first place.

This is the part to get precise about: indexing does not disappear on Canton. It relocates. When you genuinely need fast historical or analytical queries, the kind the live ACS is not optimized for, Canton's answer is the Participant Query Store (PQS). (The acronym is sometimes expanded as "Participant Query Service"; current docs say "Store.") PQS projects ledger data into PostgreSQL so you can run real SQL against history. The PQS documentation covers the setup. PQS is, functionally, an indexer. It is just a private, per-participant one rather than a public service.


So the shape difference is not "indexer versus no indexer." It is:


Honest framing. This is not strictly better. The Graph hands you an ecosystem: hosted infrastructure, pre-built subgraphs, a query language a thousand tutorials already cover. Canton hands you the responsibility. For an institution, owning that layer is a feature; they did not want their transaction history on someone else's public index anyway. For a small team shipping fast, it is one more piece of undifferentiated plumbing you own instead of rent. The architecture removes the need for a public indexer; it does not remove the work of indexing.

How wallets connect

Wallet connection on Canton changed more in the last six months than any other part of this stack, so it is worth separating what it used to be from what it is now.

Start with identity, because it is not an address. On EVM, "connect wallet" means a dApp learns an address and gets a way to ask the wallet to sign. The address is self-sovereign: a key pair, valid everywhere, allocated by nobody. A Canton Party is none of those things. It is an allocated entity hosted on a participant node, not derived from a key pair. So connecting a wallet on Canton is not just proving you hold a key. It is attaching a hosted identity to your dApp, and that gap is most of why the wallet story took longer to settle than the rest of the stack.

For a while it did not settle at all. Before a standard existed, dApp-to-wallet interaction was backend-heavy and bespoke: signing happened inside the app provider's own infrastructure, or through a custom enterprise-signer integration wired to a participant node. That was fine for the first wave of institutional apps, where the signer was a known custody system anyway. It gave you nothing like Ethereum's one-click connect experience, and it did not let an arbitrary wallet talk to an arbitrary dApp.

CIP-0103 is what changed that. It is Canton's vendor-neutral dApp API, the rough equivalent of EIP-1193, approved in late January 2026 and authored by Digital Asset and PixelPlex. In JSON-RPC 2.0 conventions it specifies how a dApp connects to a wallet, prepares and signs transactions (prepareExecute), signs arbitrary messages, subscribes to ledger events, and proxies Ledger API calls when it needs to. The whole point is decoupling: the dApp stops caring which wallet or key-management system is on the other end.

The SDKs that implement it sit under the @canton-network npm scope. @canton-network/dapp-sdk is the browser-side package for dApp developers; it runs the CIP-0103 client and talks to a Wallet Gateway. @canton-network/wallet-sdk is the lower-level package for teams building wallets. Build an app and you import the first. The second is for wallet vendors.

WalletConnect went live on Canton in April 2026, which lets the broader WalletConnect-compatible wallet ecosystem reach Canton dApps, alongside the native CIP-0103 path, not instead of it. Read "live" honestly, though. The integration shipped this spring, and the marketing line about hundreds of compatible wallets describes reach the standard could have, not an installed base of Canton users it already has.

As for the wallets themselves: on the self-custodial side, Bron, Console (Digital Asset's developer-oriented wallet), Canton Loop (from Five North Digital), and Nightly support Canton. Institutional custody and signing run through providers like Fireblocks, Dfns, and Blockdaemon. Most production Canton apps today still lean on that institutional path. The self-custodial connect-wallet flow is real and standardized now, but it is months old, not years.

One caveat: Zenith changes the wallet story. Zenith is the third-party EVM execution layer on Canton, built by ZkCloud rather than by Digital Asset. An app deployed on Zenith connects wallets the EVM way: MetaMask, standard EVM RPC, the wagmi-shaped stack. So there are two wallet stories on Canton now, and conflating them will burn you. A Daml-native app uses Parties, CIP-0103, and the dApp SDK. A Zenith app uses addresses and the EVM wallet stack. Which one is yours depends on which layer you built on.

What it means

For EVM builders. The read path and the wallet connection are not the parts of your app you port. They are parts you redesign. Three concrete planning items. First, budget for an app backend; the backendless frontend-to-chain pattern does not carry over for Daml-native apps. Second, do not go looking for The Graph. Decide early whether your historical-query needs are met by the live ACS, by a Participant Query Store, or by a light indexer you build on your backend, and own that decision. Third, identity is a hosted Party, not a key pair; the "connect wallet" UX is converging fast via CIP-0103 and WalletConnect, but it is months old, not years old. If you are building on Zenith instead, most of this inverts. You keep the EVM stack, and the Canton-specific plumbing only appears at the boundary where you compose with Daml apps.

For Canton tooling. The wallet gap is closing quickly: CIP-0103 gave the ecosystem a standard, WalletConnect gave it reach, and the SDKs are published. The data-access story is more philosophical. The absence of a public indexing marketplace is not a missing feature. Privacy means it may never exist, and that is consistent with the network's design. But the ergonomics of per-app indexing could be better, and that is a fair thing to ask for. The layer works. It just lacks the polished shared infrastructure an EVM developer reaches for without thinking.

Point-in-time caveat. This is a snapshot as of late May 2026. CIP-0103 was approved in January, WalletConnect landed in April, the unified developer docs went live days ago, and Zenith's mainnet is not yet live. Every one of those is recent enough that a single release cycle could change the specifics. The architecture, partitioned and party-scoped data behind participant nodes, is the stable part. The tooling on top of it is moving fast.

The frontier worth watching is the seam. As Daml-native apps and Zenith/EVM apps start composing with each other, a single user-facing dApp may need to read data from both a party-scoped Ledger API and an EVM-shaped state, and connect a wallet that means two different things on two sides of an external_call(). Nobody has a clean stack for that yet. That is the interesting unsolved problem, and it is downstream of everything in this post.

A point-in-time analysis as of May 20, 2026. For the most current state, consult docs.canton.network.

Author 

Nick Sawinyh is a Web3 BD and product strategist, 10+ years in crypto, specializing in turning complex technical products into clear strategy. He builds IRSForge, an interest-rate-swap platform on Canton.

Canton NetworkDamlLedger APICanton dAppsEVMParticipant NodesPQSCIP-0103WalletConnectZenith
Source: Canton Network