System Architecture

Dualis Finance is built as a modular monorepo that bridges traditional financial infrastructure with decentralized smart contracts on the Canton Network. This page describes the system topology, technology stack, and data flow for core operations.

High-Level Overview

The platform follows a hybrid on-chain/off-chain architecture. User-facing operations originate in the frontend, pass through a centralized API layer for validation and orchestration, and ultimately settle on Canton via DAML smart contracts. Off-chain components handle rate computation, indexing, and caching, while Canton provides atomic settlement, privacy-preserving execution, and an immutable audit trail.

Hybrid Architecture
Dualis Finance intentionally separates compute-intensive operations (interest accrual, risk calculations) from settlement (Canton). This design keeps gas costs predictable while preserving the integrity guarantees of a distributed ledger.

Technology Stack

LayerTechnologyVersionPurpose
FrontendNext.js / React14.2 / 18Server-rendered UI with client-side state management
State ManagementZustand4.xLightweight global state for wallet, positions, pools
StylingTailwind CSS / Radix UI3.x / 1.xUtility-first styling with accessible primitives
API ServerFastify5.7High-performance HTTP + WebSocket API
ORMDrizzle ORM0.3xType-safe SQL with zero runtime overhead
DatabasePostgreSQL1663 tables covering positions, pools, users, credit, governance
Cache / QueueRedis / BullMQ7.xRate caching, session store, and background job processing
Smart ContractsDAML (Canton)3.4 / LF 2.138 templates for lending, collateral, liquidation, governance
TestingVitest1.xUnit and integration tests (230+ shared package tests)

Monorepo Structure

The codebase is organized as a pnpm workspace managed by Turborepo. Each package has a clear boundary and well-defined dependency graph:

PackagePathDescription
frontendpackages/frontendNext.js 14 application with App Router, Zustand stores, and Tailwind
apipackages/apiFastify server with Drizzle ORM, BullMQ jobs, Canton client
sharedpackages/sharedFinancial math engine, type definitions, formatters, constants
cantonpackages/cantonDAML source (25 modules), DAR build scripts, deployment tooling
configpackages/configShared ESLint, TypeScript, and Tailwind configuration

The shared package must build first because both api and frontend depend on its exported types and utility functions. Turborepo handles this ordering automatically via the dependency graph.

Data Flow: Supply Operation

To illustrate how the layers interact, here is the step-by-step data flow when a user supplies USDC to a lending pool:

  1. Frontend: The user connects their Canton wallet via PartyLayer and selects an amount to supply. The frontend validates the input and sends a POST /api/v1/pools/:id/supply request.
  2. API — Validation: Fastify middleware authenticates the session, checks KYC status, and validates the amount against pool limits and the user's wallet balance.
  3. API — Rate Computation: The shared math engine calculates the current supply index using the jump rate model. The accrued index is persisted to PostgreSQL and cached in Redis for subsequent reads.
  4. Canton — Settlement: The API submits a DAML command to exercise the Supply choice on the LendingPool contract. Canton atomically mints pool tokens (dTokens) and transfers the underlying asset.
  5. API — Indexing: A BullMQ worker listens for the Canton transaction result, updates the position in PostgreSQL, and pushes a WebSocket event to the frontend.
  6. Frontend — Update: The Zustand store receives the WebSocket event, updates the user's position, and re-renders the portfolio view with the new supply balance and projected APY.
Atomic Settlement
Canton's DAML runtime guarantees that the token mint and asset transfer in step 4 either both succeed or both fail. There is no partial state, eliminating an entire class of re-entrancy and front-running vulnerabilities common on EVM chains.

Infrastructure

In the current devnet deployment, all services run on a single host behind an Nginx reverse proxy with Let's Encrypt TLS. The Canton validator node (v0.5.12) operates within a Docker network, with the JSON API accessible on port 7575 and gRPC on port 5001. PostgreSQL, Redis, the API server, and the frontend each run in dedicated containers orchestrated by Docker Compose.

For mainnet, the architecture is designed to scale horizontally: the API layer is stateless (session state lives in Redis), BullMQ workers can be replicated across nodes, and PostgreSQL supports read replicas for query-heavy paths such as portfolio analytics and pool listings.