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:
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:
| Function | Module | Description |
|---|---|---|
calculateAPY | math | Converts APR to APY using continuous compounding |
calculateBorrowRate | math | Jump Rate Model — returns borrow rate for a given utilization |
calculateSupplyRate | math | Derives supply rate from borrow rate, utilization, and reserve factor |
calculateHealthFactor | math | Computes health factor from collateral and debt positions |
calculateUtilization | math | Returns pool utilization ratio (totalBorrow / totalSupply) |
calculateLiquidationThreshold | math | Determines the price level at which a position becomes liquidatable |
formatCurrency | format | Formats a number as a USD string with proper separators |
formatPercentage | format | Formats a decimal as a percentage string (e.g., 0.05 to "5.00%") |
formatCompact | format | Formats 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:
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"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:
pnpm --filter @dualis/shared testdist/ 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).