SDK — @dualis/shared

The @dualis/shared package is the TypeScript SDK at the heart of Dualis Finance. It provides 94 shared type definitions, 30+ utility functions for financial math, and configuration constants used by both the API server and the frontend.

Overview

Rather than duplicating financial logic across packages, Dualis centralizes all rate calculations, health factor computations, formatting utilities, and protocol configuration in a single shared package. This guarantees that the numbers displayed in the frontend exactly match those computed on the server and validated on Canton.

The package is built with TypeScript and outputs CommonJS modules. It must build before the api and frontend packages, which Turborepo handles automatically via the dependency graph.

Installation

Within the monorepo, the shared package is already linked as a workspace dependency. If you need to reference it explicitly:

Terminal
pnpm --filter @dualis/api add @dualis/shared@workspace:*
pnpm --filter @dualis/frontend add @dualis/shared@workspace:*

Key Exports

The package exposes several categories of utilities:

FunctionModuleDescription
calculateAPYmathConverts APR to APY using continuous compounding
calculateBorrowRatemathJump Rate Model — returns borrow rate for a given utilization
calculateSupplyRatemathDerives supply rate from borrow rate, utilization, and reserve factor
calculateHealthFactormathComputes health factor from collateral and debt positions
calculateUtilizationmathReturns pool utilization ratio (totalBorrow / totalSupply)
calculateLiquidationThresholdmathDetermines the price level at which a position becomes liquidatable
formatCurrencyformatFormats a number as a USD string with proper separators
formatPercentageformatFormats a decimal as a percentage string (e.g., 0.05 to "5.00%")
formatCompactformatFormats large numbers with K/M/B suffixes

Usage Example

The following example demonstrates how to compute pool rates and a borrower's health factor using the shared math engine:

example.ts
import {
  calculateBorrowRate,
  calculateSupplyRate,
  calculateAPY,
  calculateHealthFactor,
  formatCurrency,
  formatPercentage,
} from '@dualis/shared';

// Pool parameters
const utilization = 0.72; // 72% utilized
const rateModel = {
  baseRate: 0.02,
  multiplier: 0.1,
  jumpMultiplier: 0.8,
  kink: 0.8,
};
const reserveFactor = 0.1;

// Calculate rates
const borrowRate = calculateBorrowRate(utilization, rateModel);
const supplyRate = calculateSupplyRate(borrowRate, utilization, reserveFactor);
const borrowAPY = calculateAPY(borrowRate);
const supplyAPY = calculateAPY(supplyRate);

console.log(`Borrow APY: ${formatPercentage(borrowAPY)}`);
// => "Borrow APY: 9.65%"

console.log(`Supply APY: ${formatPercentage(supplyAPY)}`);
// => "Supply APY: 6.25%"

// Health factor calculation (new overload)
const healthResult = calculateHealthFactor(
  [
    { asset: 'ETH', valueUSD: 10000, collateralFactor: 0.82 },
    { asset: 'BTC', valueUSD: 25000, collateralFactor: 0.78 },
  ],
  [
    { asset: 'USDC', valueUSD: 15000 },
  ]
);

console.log(`Health Factor: ${healthResult.healthFactor.toFixed(2)}`);
// => "Health Factor: 1.85"

console.log(`Liquidation at: ${formatCurrency(healthResult.liquidationThresholdUSD)}`);
// => "Liquidation at: $18,292.68"
Overloaded Signatures
The calculateHealthFactor function has two overloads. The legacy signature accepts raw arrays and returns a plain number. The new signature accepts typed CollateralPositionInput[] and DebtPositionInput[] and returns a HealthFactorResult object. If you use the legacy overload, cast the return value: as number.

Configuration Exports

The config module exports protocol parameters that mirror the on-chain DAML contract state:

  • Rate Models — Jump Rate Model parameters per asset class
  • Collateral Parameters — LTV caps, liquidation bonuses, and haircuts by tier (crypto: 0%, RWA: 5%, TIFA: 20%)
  • Credit Tiers — Diamond, Gold, Silver, Bronze, and Unrated with corresponding rate discounts and LTV caps

Testing

The shared package ships with 230+ tests covering all math functions, edge cases, and configuration invariants. Run them with Vitest:

Terminal
pnpm --filter @dualis/shared test
Stale Builds
If tests pass locally but fail in CI, or vice versa, the dist/ directory may be stale. Delete it and rebuild: rm -rf packages/shared/dist && pnpm run build.

Type Definitions

The package exports 94 TypeScript types covering every domain entity: pools, positions, users, credit tiers, governance proposals, oracle prices, and more. These types are the single source of truth shared between the Fastify API routes (Zod 4 validation schemas) and the Next.js 14.2 frontend (Zustand 5 stores).