@kasufinance/kasu-sdk 2.4.1 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +189 -253
  3. package/dist/bundle.cjs.js +4777 -1794
  4. package/dist/bundle.esm.js +4766 -1816
  5. package/dist/domain/deposit-bounds.d.ts +62 -0
  6. package/dist/domain/deposit-bounds.js +84 -0
  7. package/dist/domain/deposit-bounds.js.map +1 -0
  8. package/dist/domain/deposit-bounds.test.js +135 -0
  9. package/dist/domain/deposit-bounds.test.js.map +1 -0
  10. package/dist/domain/index.d.ts +26 -0
  11. package/dist/domain/index.js +23 -0
  12. package/dist/domain/index.js.map +1 -0
  13. package/dist/domain/partners.d.ts +34 -0
  14. package/dist/domain/partners.js +43 -0
  15. package/dist/domain/partners.js.map +1 -0
  16. package/dist/domain/partners.test.d.ts +1 -0
  17. package/dist/domain/partners.test.js +53 -0
  18. package/dist/domain/partners.test.js.map +1 -0
  19. package/dist/domain/pools.d.ts +70 -0
  20. package/dist/domain/pools.js +134 -0
  21. package/dist/domain/pools.js.map +1 -0
  22. package/dist/domain/pools.test.d.ts +1 -0
  23. package/dist/domain/pools.test.js +184 -0
  24. package/dist/domain/pools.test.js.map +1 -0
  25. package/dist/domain/rates.d.ts +83 -0
  26. package/dist/domain/rates.js +120 -0
  27. package/dist/domain/rates.js.map +1 -0
  28. package/dist/domain/rates.test.d.ts +1 -0
  29. package/dist/domain/rates.test.js +181 -0
  30. package/dist/domain/rates.test.js.map +1 -0
  31. package/dist/domain/tranche-display-name.d.ts +17 -0
  32. package/dist/domain/tranche-display-name.js +30 -0
  33. package/dist/domain/tranche-display-name.js.map +1 -0
  34. package/dist/domain/tranche-display-name.test.d.ts +1 -0
  35. package/dist/domain/tranche-display-name.test.js +58 -0
  36. package/dist/domain/tranche-display-name.test.js.map +1 -0
  37. package/dist/domain/tranches.d.ts +89 -0
  38. package/dist/domain/tranches.js +150 -0
  39. package/dist/domain/tranches.js.map +1 -0
  40. package/dist/domain/tranches.test.d.ts +1 -0
  41. package/dist/domain/tranches.test.js +206 -0
  42. package/dist/domain/tranches.test.js.map +1 -0
  43. package/dist/facade/chain-configs.js +54 -0
  44. package/dist/facade/chain-configs.js.map +1 -1
  45. package/dist/facade/config.test.d.ts +1 -0
  46. package/dist/facade/config.test.js +216 -0
  47. package/dist/facade/config.test.js.map +1 -0
  48. package/dist/facade/deposits.d.ts +22 -1
  49. package/dist/facade/deposits.js +24 -1
  50. package/dist/facade/deposits.js.map +1 -1
  51. package/dist/facade/facade.test.d.ts +1 -0
  52. package/dist/{tests → facade}/facade.test.js +10 -5
  53. package/dist/facade/facade.test.js.map +1 -0
  54. package/dist/facade/index.d.ts +2 -1
  55. package/dist/facade/index.js +2 -0
  56. package/dist/facade/index.js.map +1 -1
  57. package/dist/facade/kasu.d.ts +49 -8
  58. package/dist/facade/kasu.js +117 -13
  59. package/dist/facade/kasu.js.map +1 -1
  60. package/dist/facade/strategies.d.ts +25 -0
  61. package/dist/facade/strategies.js +36 -0
  62. package/dist/facade/strategies.js.map +1 -1
  63. package/dist/facade/types.d.ts +57 -2
  64. package/dist/facade/unused-pool-ids.d.ts +31 -0
  65. package/dist/facade/unused-pool-ids.js +57 -0
  66. package/dist/facade/unused-pool-ids.js.map +1 -0
  67. package/dist/index.d.ts +1 -0
  68. package/dist/index.js +5 -0
  69. package/dist/index.js.map +1 -1
  70. package/dist/sdk-config.d.ts +11 -0
  71. package/dist/sdk-config.js +8 -1
  72. package/dist/sdk-config.js.map +1 -1
  73. package/dist/services/DataService/data-service.d.ts +5 -0
  74. package/dist/services/DataService/data-service.js +7 -2
  75. package/dist/services/DataService/data-service.js.map +1 -1
  76. package/dist/services/Locking/calculate-apy.test.d.ts +1 -0
  77. package/dist/services/Locking/calculate-apy.test.js +41 -0
  78. package/dist/services/Locking/calculate-apy.test.js.map +1 -0
  79. package/dist/services/Locking/locking.d.ts +14 -0
  80. package/dist/services/Locking/locking.js +16 -2
  81. package/dist/services/Locking/locking.js.map +1 -1
  82. package/dist/tests/sample.test.js +46 -25
  83. package/dist/tests/sample.test.js.map +1 -1
  84. package/package.json +44 -13
  85. package/src/domain/deposit-bounds.test.ts +200 -0
  86. package/src/domain/deposit-bounds.ts +118 -0
  87. package/src/domain/index.ts +70 -0
  88. package/src/domain/partners.test.ts +83 -0
  89. package/src/domain/partners.ts +66 -0
  90. package/src/domain/pools.test.ts +260 -0
  91. package/src/domain/pools.ts +150 -0
  92. package/src/domain/rates.test.ts +254 -0
  93. package/src/domain/rates.ts +120 -0
  94. package/src/domain/tranche-display-name.test.ts +96 -0
  95. package/src/domain/tranche-display-name.ts +34 -0
  96. package/src/domain/tranches.test.ts +300 -0
  97. package/src/domain/tranches.ts +177 -0
  98. package/src/facade/chain-configs.ts +55 -0
  99. package/src/facade/config.test.ts +265 -0
  100. package/src/facade/deposits.ts +25 -0
  101. package/src/{tests → facade}/facade.test.ts +10 -5
  102. package/src/facade/index.ts +4 -0
  103. package/src/facade/kasu.ts +155 -12
  104. package/src/facade/strategies.ts +36 -0
  105. package/src/facade/types.ts +60 -2
  106. package/src/facade/unused-pool-ids.ts +59 -0
  107. package/src/index.ts +6 -0
  108. package/src/sdk-config.ts +19 -1
  109. package/src/services/DataService/data-service.ts +7 -2
  110. package/src/services/Locking/calculate-apy.test.ts +52 -0
  111. package/src/services/Locking/locking.ts +16 -2
  112. package/src/tests/sample.test.ts +54 -33
  113. package/.eslintignore +0 -3
  114. package/.eslintrc.json +0 -90
  115. package/.github/workflows/ci.yml +0 -39
  116. package/.github/workflows/gitleaks.yml +0 -22
  117. package/.github/workflows/main.yml +0 -41
  118. package/.gitleaksignore +0 -19
  119. package/.prettierignore +0 -3
  120. package/.prettierrc +0 -28
  121. package/CLAUDE.md +0 -410
  122. package/copy-abi.ps1 +0 -15
  123. package/dist/tests/facade.test.js.map +0 -1
  124. package/index.ts +0 -2
  125. package/jest-config.ts +0 -9
  126. package/jest.config.js +0 -8
  127. package/rollup.config.js +0 -45
  128. package/tsconfig.json +0 -27
  129. /package/dist/{tests/facade.test.d.ts → domain/deposit-bounds.test.d.ts} +0 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kasu Finance
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,313 +1,249 @@
1
1
  # Kasu SDK
2
2
 
3
- `@kasufinance/kasu-sdk` is the shared TypeScript/JavaScript toolkit used by Kasu
4
- frontends to interact with the Kasu protocol. It wraps the core smart
5
- contracts, subgraphs, and Directus CMS in a single object so that dapps can
6
- query pool data, compute portfolio statistics, and submit locking or lending
7
- transactions without re-implementing the plumbing.
3
+ [![npm version](https://img.shields.io/npm/v/@kasufinance/kasu-sdk.svg)](https://www.npmjs.com/package/@kasufinance/kasu-sdk)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
5
+ [![CI](https://github.com/Kasu-Finance/kasu-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/Kasu-Finance/kasu-sdk/actions/workflows/ci.yml)
6
+
7
+ `@kasufinance/kasu-sdk` is the TypeScript SDK for the Kasu Finance protocol —
8
+ real-world credit funded on-chain. It wraps the core contracts, the subgraphs
9
+ and the CMS behind one object, so an application can list lending strategies,
10
+ read a lender's positions and submit KYC-gated deposits and withdrawals without
11
+ re-implementing the plumbing. It also ships a pure domain layer: the rate,
12
+ tranche and pool rules every Kasu frontend agrees on, as numbers rather than
13
+ copy.
8
14
 
9
15
  ## Installation
10
16
 
11
17
  ```bash
12
18
  npm install @kasufinance/kasu-sdk
13
- # or
14
- yarn add @kasufinance/kasu-sdk
15
19
  ```
16
20
 
17
- The SDK is built against `ethers@5`, so make sure your project already depends
18
- on it (or install it alongside the SDK).
21
+ The SDK is built against **ethers v5** and resolves to the ethers already in
22
+ your project — it is marked external in the published bundle, so there is only
23
+ ever one copy in the tree. ethers v6 is not supported.
19
24
 
20
- ## Runtime requirements
25
+ ## Requirements
21
26
 
22
- - **Node or browser environment with fetch/XHR** when using the SDK on the
23
- server (Next.js `app` router actions, serverless functions, etc.) you must
24
- provide an XHR implementation because Directus uses it under the hood:
27
+ - **Node 18 or later**, or any runtime with a global `fetch` (all modern
28
+ browsers, Deno, Bun, edge runtimes). No XHR polyfill is needed.
29
+ - **A provider or signer** optional. Without one the SDK builds a read-only
30
+ provider from the chain's default RPC; pass your own to control the endpoint,
31
+ and a signer to send transactions.
25
32
 
26
- ```ts
27
- // server.ts
28
- import { XMLHttpRequest } from 'xhr2';
29
- global.XMLHttpRequest = XMLHttpRequest;
30
- ```
31
-
32
- - **Provider/Signer** – pass an `ethers` `Signer` for write actions or a
33
- `Provider` for read-only usage. The SDK does not create its own provider,
34
- so you are free to reuse whatever the dapp already uses.
35
-
36
- ## Integrator Quick Start
37
-
38
- The simplest way to use the SDK is via the `Kasu` facade, which bundles built-in
39
- chain configs and provides three domain-specific interfaces:
33
+ ## Quick start
40
34
 
41
35
  ```ts
42
- import { JsonRpcProvider } from '@ethersproject/providers';
43
- import { Kasu } from '@kasufinance/kasu-sdk';
44
-
45
- const provider = new JsonRpcProvider('https://mainnet.base.org');
46
- const kasu = Kasu.create({ chain: 'base', signerOrProvider: provider });
47
-
48
- // 1. Browse lending strategies
49
- const strategies = await kasu.strategies.getAll();
36
+ import {
37
+ Kasu,
38
+ fetchUnusedPoolIds,
39
+ netEffectiveApy,
40
+ } from '@kasufinance/kasu-sdk';
41
+ import { parseUnits } from 'ethers/lib/utils';
42
+
43
+ // Read-only. No wallet, no provider — uses the chain's default public RPC.
44
+ const kasu = Kasu.create({ chain: 'base' });
45
+
46
+ // The strategies a lender should see: active, not oversubscribed, capacity first.
47
+ const strategies = await kasu.strategies.getVisible();
48
+
49
+ // Rates are NET of the platform performance fee. `getPerformanceFeePercent()`
50
+ // returns a percentage in 0..100 (10 = ten percent), never a fraction.
51
+ const feePercent = await kasu.strategies.getPerformanceFeePercent();
50
52
  for (const s of strategies) {
51
- console.log(s.name, `${(s.apy * 100).toFixed(1)}% APY`, `TVL: $${s.tvl.total}`);
53
+ const net = netEffectiveApy(s.tranches[0].apy, feePercent);
54
+ console.log(s.name, `${(net * 100).toFixed(2)}% p.a. net`);
52
55
  }
53
56
 
54
- // 2. Get a single strategy and check deposit limits
55
- const strategy = await kasu.strategies.getById(poolId);
56
- const limits = kasu.strategies.calculateDepositLimits(strategy.tranches[0]);
57
- console.log(`Min: ${limits.min} USDC, Max: ${limits.max} USDC`);
58
-
59
- // 3. Deposit (requires a Signer and KYC signature from Nexera)
60
- const kycParams = kasu.deposits.buildKycParams('0xYourAddress...');
61
- // ... obtain kycSignature via your backend + Nexera signing service ...
62
- const tx = await kasu.deposits.deposit({
63
- poolId: strategy.id,
64
- trancheId: strategy.tranches[0].id,
65
- amount: parseUnits('1000', 6), // 1000 USDC
66
- kycSignature: { blockExpiration, signature },
57
+ // Optional: hide pools that have no published content yet.
58
+ const readOnly = Kasu.create({
59
+ chain: 'base',
60
+ configOverrides: { UNUSED_LENDING_POOL_IDS: await fetchUnusedPoolIds() },
67
61
  });
68
62
 
69
- // 4. Check user positions and yield
70
- const positions = await kasu.portfolio.getPositions('0xUserAddress...');
71
- console.log(positions.summary.current.totalLendingPoolInvestments);
72
-
73
- // 5. Withdraw
74
- const withdrawTx = await kasu.deposits.withdraw({
75
- poolId: strategy.id,
76
- trancheId: strategy.tranches[0].id,
77
- amount: parseUnits('500', 6),
63
+ // Writes need a signer. `connect` returns a NEW instance; the read-only one
64
+ // keeps working.
65
+ // signer: an ethers v5 Signer from your wallet library
66
+ const signed = kasu.connect(signer);
67
+ const strategy = strategies[0];
68
+ const kycParams = signed.deposits.buildKycParams('0xYourAddress');
69
+ // blockExpiration, signature: obtained from your KYC backend using kycParams
70
+ const tx = await signed.deposits.deposit({
71
+ poolId: strategy.id,
72
+ trancheId: strategy.tranches[0].id,
73
+ amount: parseUnits('1000', kasu.chainConfig.stableAsset.decimals),
74
+ kycSignature: { blockExpiration, signature },
78
75
  });
79
- ```
80
-
81
- ### Supported chains
82
-
83
- | Chain | Usage | Deployment Type |
84
- |-------|-------|-----------------|
85
- | Base | `Kasu.create({ chain: 'base', ... })` | Full (KSU + lending) |
86
- | XDC | `Kasu.create({ chain: 'xdc', ... })` | Lite (lending only) |
87
- | Plume | `Kasu.create({ chain: 'plume', ... })` | Lite (lending only) |
88
76
 
89
- You can also provide a custom `ChainConfigEntry` instead of a chain name:
90
-
91
- ```ts
92
- const kasu = Kasu.create({
93
- chain: { chainId: 123, name: 'MyChain', isLiteDeployment: true, contracts: { ... }, ... },
94
- signerOrProvider: provider,
95
- });
77
+ // Positions and yield (read-only is enough).
78
+ const positions = await kasu.portfolio.getPositions('0xUserAddress');
96
79
  ```
97
80
 
98
- ### Facade API overview
81
+ Calling a write method on a read-only instance throws before it touches the
82
+ contract: `Kasu: this instance is read-only; call kasu.connect(signer) first`.
99
83
 
100
- | Facade | Methods | Purpose |
101
- |--------|---------|---------|
102
- | `kasu.strategies` | `getAll()`, `getById()`, `getPlatformStats()`, `calculateDepositLimits()` | Browse pools, APY, capacity |
103
- | `kasu.deposits` | `deposit()`, `withdraw()`, `withdrawMax()`, `buildKycParams()`, `isClearingPending()` | Submit transactions |
104
- | `kasu.portfolio` | `getPositions()`, `getTransactionHistory()` | User balances, yield, history |
84
+ ## Supported deployments
105
85
 
106
- For advanced use cases (locking, swaps, NFTs), access the underlying services directly:
86
+ Each deployment lends in exactly one stable token. A different token means a
87
+ separate deployment, not a second vault.
107
88
 
108
- ```ts
109
- const locks = await kasu.services.Locking.getUserLocks('0x...');
110
- ```
111
-
112
- ---
89
+ | Chain key | Network | Chain ID | Type | Stable asset | Subgraph | Status |
90
+ | ---------- | ------------------ | -------- | ---- | ------------ | ---------------------- | ----------------------------- |
91
+ | `base` | Base Mainnet | 8453 | Full | USDC | `kasu-base/v1.0.13` | Production |
92
+ | `xdc` | XDC Mainnet | 50 | Lite | AUDD | `kasu-xdc/v1.0.0` | Production |
93
+ | `xdc-usdc` | XDC Mainnet (USDC) | 50 | Lite | USDC | `kasu-xdc-usdc/v1.0.0` | Production, separate stack |
94
+ | `plume` | Plume Mainnet | 98866 | Lite | pUSD | legacy project | Retired — frozen history only |
113
95
 
114
- ## Low-level SDK Configuration
96
+ Full deployments have the KSU token, locking and loyalty rewards; Lite
97
+ deployments have lending only. `plume` is wound down: it has no default RPC, so
98
+ a read-only `Kasu.create({ chain: 'plume' })` throws — pass your own provider to
99
+ read its history.
115
100
 
116
- The sections below show how to configure the SDK manually using `SdkConfig` and
117
- `KasuSdk` directly. Most integrators should use the `Kasu` facade above instead.
118
-
119
- ### Full Deployment (Base)
120
-
121
- Full deployments have all features including KSU token, locking, and loyalty rewards:
101
+ Every entry lives in `CHAIN_CONFIGS`, including contract addresses, subgraph
102
+ URLs, the stable asset and public RPC defaults:
122
103
 
123
104
  ```ts
124
- import { JsonRpcProvider } from '@ethersproject/providers';
125
- import { KasuSdk, SdkConfig } from '@kasufinance/kasu-sdk';
126
- import { XMLHttpRequest } from 'xhr2';
127
-
128
- global.XMLHttpRequest = XMLHttpRequest; // required on Node runtimes
105
+ import { CHAIN_CONFIGS } from '@kasufinance/kasu-sdk';
129
106
 
130
- const provider = new JsonRpcProvider(process.env.RPC_URL!, { skipFetchSetup: true });
131
-
132
- const config = new SdkConfig({
133
- contracts: {
134
- KSUToken: '0x…', // Required for Full deployment
135
- IKSULocking: '0x…',
136
- IKSULockBonus: '0x…',
137
- UserManager: '0x…',
138
- LendingPoolManager: '0x…',
139
- KasuAllowList: '0x…',
140
- SystemVariables: '0x…',
141
- UserLoyaltyRewards: '0x…',
142
- KsuPrice: '0x…',
143
- ClearingCoordinator: '0x…',
144
- KasuNFTs: '0x…', // Required for Full deployment
145
- ExternalTVL: '0x…',
146
- },
147
- UNUSED_LENDING_POOL_IDS: [''],
148
- directusUrl: 'https://kasu-finance.directus.app/',
149
- subgraphUrl: 'https://api.goldsky.com/.../kasu-base/v1/gn',
150
- isLiteDeployment: false, // Full deployment (default)
151
- });
152
-
153
- export const kasuSdk = new KasuSdk(config, provider);
107
+ CHAIN_CONFIGS.base.stableAsset; // { address, symbol, name, decimals, currencyCode }
108
+ CHAIN_CONFIGS.xdc.rpcUrls; // starting preference only — override with your own
154
109
  ```
155
110
 
156
- ### Lite Deployment (XDC, Plume)
157
-
158
- Lite deployments don't have KSU token, locking, or loyalty features. They still support
159
- KYC/KYB gated deposits and all lending pool functionality:
111
+ You can also pass a whole `ChainConfigEntry` instead of a chain key.
112
+
113
+ ## Facade API overview
114
+
115
+ | Facade | Methods | Purpose |
116
+ | ----------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
117
+ | `kasu.strategies` | `getAll()`, `getVisible()`, `getById()`, `getPlatformStats()`, `getPerformanceFeePercent()`, `calculateDepositLimits()` | Browse pools, APY, capacity |
118
+ | `kasu.deposits` | `deposit()`, `withdraw()`, `withdrawMax()`, `buildKycParams()`, `isClearingPending()` | Submit transactions |
119
+ | `kasu.portfolio` | `getPositions()`, `getTransactionHistory()` | Lender balances, yield, history |
120
+
121
+ On the instance itself: `kasu.connect(signer)`, `kasu.isReadOnly`,
122
+ `kasu.provider`, `kasu.chainConfig`, `kasu.isLiteDeployment`, and
123
+ `kasu.services` for the low-level services below.
124
+
125
+ `fetchUnusedPoolIds(directusUrl?)` reads the pool ids that are configured but
126
+ not yet published, for `configOverrides.UNUSED_LENDING_POOL_IDS`. It may
127
+ return an empty list; pass it straight through. `SdkConfig` normalises an empty
128
+ exclusion list to `['']`, because the subgraph reads `id_not_in: []` as "match
129
+ nothing" and would otherwise hide every pool.
130
+
131
+ ## Domain helpers
132
+
133
+ Pure functions shared by every Kasu frontend. They return **numbers and codes,
134
+ never copy** — no locale, no `Intl`, no user-facing strings — so each
135
+ application formats them in its own design system and language.
136
+
137
+ | Module | Exports |
138
+ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
139
+ | Rates | `EPOCHS_IN_YEAR`, `epochRateToApy`, `apyToEpochRate`, `netEffectiveApy` |
140
+ | Tranches | `trancheHasCapacity`, `poolAllTranchesFull`, `derivePoolStatus`, `trancheRiskRank`, `compareTrancheSeniority`, `pickDefaultTrancheId`, `trancheApyBounds`, `netTrancheApyBounds`, `MIN_TRANCHE_CAPACITY` |
141
+ | Deposit bounds | `resolveDepositBounds`, `resolveBoundShortcuts`, `isBelowMinimumCapacity`, `parseTrancheBound`, `floorToCents`, `ceilToCents` |
142
+ | Pools | `selectVisiblePools`, `poolMaxApy`, `pickHighestYieldTranche`, `maxNetRateCeiling` |
143
+ | Partners | `getCreditOriginator`, `getInstitutionalLender`, `APXIUM`, `INVOICEMATE`, `RIXON_CAPITAL` |
144
+ | Tranche display name | `getTrancheDisplayName`, `UPPER_MEZZANINE` |
145
+
146
+ Two rules worth knowing:
147
+
148
+ - **Rates fail closed.** `netEffectiveApy`, `netTrancheApyBounds` and
149
+ `maxNetRateCeiling` return `NaN` / `null` rather than a plausible-looking
150
+ wrong number when an input is out of domain. Render that as "no rate", never
151
+ as zero, and never substitute a fee of `0` for one you have not loaded — a
152
+ fee-less rate overstates what a lender earns.
153
+ - **The tranche rename is display-only.** `getTrancheDisplayName` maps the
154
+ `Senior` tranche to "Upper Mezzanine" on Apxium strategies, because the true
155
+ senior position is held by an institutional lender. Call it at the view
156
+ boundary only: ranking, matching and sorting keep the raw on-chain name.
157
+
158
+ ## Low-level `KasuSdk`
159
+
160
+ Most integrators should use the `Kasu` facade. For locking, swaps, NFTs and
161
+ anything the facade does not cover, reach the services directly — either
162
+ through `kasu.services` or by constructing `KasuSdk` with your own `SdkConfig`
163
+ (see `CHAIN_CONFIGS` in `src/facade/chain-configs.ts` for a complete example of
164
+ every field).
160
165
 
161
166
  ```ts
162
- const config = new SdkConfig({
163
- contracts: {
164
- // KSUToken: undefined, // Not available on Lite
165
- IKSULocking: '0x…', // Lite version contract
166
- IKSULockBonus: '0x…', // Lite version contract
167
- UserManager: '0x…', // Lite version contract
168
- LendingPoolManager: '0x…',
169
- KasuAllowList: '0x…',
170
- SystemVariables: '0x…',
171
- UserLoyaltyRewards: '0x…', // Lite version contract
172
- KsuPrice: '0x…', // Lite version contract
173
- ClearingCoordinator: '0x…',
174
- // KasuNFTs: undefined, // Not available on Lite
175
- ExternalTVL: '0x…',
176
- },
177
- UNUSED_LENDING_POOL_IDS: [''],
178
- directusUrl: 'https://kasu-finance.directus.app/',
179
- subgraphUrl: 'https://api.goldsky.com/.../kasu-xdc/v1.0.0/gn',
180
- isLiteDeployment: true, // Lite deployment - disables KSU features
181
- });
182
-
183
- export const kasuSdk = new KasuSdk(config, provider);
184
- ```
167
+ import { KasuSdk, SdkConfig, CHAIN_CONFIGS } from '@kasufinance/kasu-sdk';
185
168
 
186
- ### Multi-Chain Configuration
187
-
188
- For frontends supporting multiple chains, create a config per chain:
189
-
190
- ```ts
191
- // config/chains.ts
192
- const chainConfigs = {
193
- base: {
194
- chainId: 8453,
195
- subgraphUrl: 'https://api.goldsky.com/.../kasu-base/v1/gn',
196
- isLiteDeployment: false,
197
- contracts: { /* Base contract addresses */ },
198
- },
199
- xdc: {
200
- chainId: 50,
201
- subgraphUrl: 'https://api.goldsky.com/.../kasu-xdc/v1.0.0/gn',
202
- isLiteDeployment: true,
203
- contracts: { /* XDC contract addresses */ },
204
- },
205
- };
206
-
207
- // Create SDK for current chain
208
- const chainConfig = chainConfigs[currentChain];
209
169
  const config = new SdkConfig({
210
- ...chainConfig,
211
- directusUrl: 'https://kasu-finance.directus.app/',
212
- UNUSED_LENDING_POOL_IDS: [''],
170
+ subgraphUrl: CHAIN_CONFIGS.base.subgraphUrl,
171
+ contracts: CHAIN_CONFIGS.base.contracts,
172
+ directusUrl: CHAIN_CONFIGS.base.directusUrl,
173
+ UNUSED_LENDING_POOL_IDS: [],
174
+ isLiteDeployment: false,
175
+ stableAssetDecimals: CHAIN_CONFIGS.base.stableAsset.decimals,
213
176
  });
214
- const sdk = new KasuSdk(config, provider);
215
- ```
216
-
217
- See `kasu-fe-next/src/config/sdk` in the Kasu frontend repository for full
218
- mainnet and testnet examples, including how Kasu fetches unused pool ids before
219
- instantiating the SDK.
220
-
221
- ### Low-level Quick start
222
177
 
223
- ```ts
224
- import { JsonRpcProvider } from '@ethersproject/providers';
225
- import { KasuSdk } from '@kasufinance/kasu-sdk';
226
-
227
- const provider = new JsonRpcProvider(RPC_URL);
178
+ // provider: an ethers v5 Provider or Signer
228
179
  const sdk = new KasuSdk(config, provider);
229
-
180
+ // currentEpochId: from `await kasu.deposits.getCurrentEpoch()`
230
181
  const pools = await sdk.DataService.getPoolOverview(currentEpochId);
231
- const lockingPeriods = await sdk.Locking.getLockPeriods();
232
- const userSummary = await sdk.Portfolio.getPortfolioSummary(userAddress);
233
- await sdk.Locking.lockKSUTokens(amountBn, lockPeriodBn); // signer required
234
182
  ```
235
183
 
236
- `KasuSdk` exposes services as properties. Each service contains the
237
- methods for a single protocol facet:
184
+ | Service | Purpose |
185
+ | ------------- | ------------------------------------------------------------------------------------------- |
186
+ | `DataService` | On-chain pool data (subgraph, external TVL) plus CMS content — descriptions, KPIs, imagery. |
187
+ | `Locking` | KSU locking: periods, projected rewards, lock/unlock, claim fees. Full deployments only. |
188
+ | `UserLending` | Deposit and withdrawal requests, transaction history, CSV builders. |
189
+ | `Portfolio` | Balances, rewards, lending totals, APY. |
190
+ | `Swapper` | Calls through the on-chain swapper. |
238
191
 
239
- | Service | Purpose |
240
- | ------------- | ------------------------------------------------------------------------------------------------------------ |
241
- | `DataService` | Aggregates on-chain data (subgraphs/external TVL) and off-chain content from Directus (pool descriptions, KPIs). |
242
- | `Locking` | High-level helpers for KSU locking: read locking periods, calculate projected rewards, lock/unlock, claim fees. |
243
- | `UserLending` | User-centric lending utilities (deposit/withdraw requests, transaction history, CSV builders). |
244
- | `Portfolio` | Portfolio snapshots: balances, rewards, lending totals, APY calculations. |
245
- | `Swapper` | Helpers for contract calls through the on-chain swapper. |
192
+ Every method is typed, so an editor discovers the shape of `PoolOverview`,
193
+ `LockPeriod`, `PortfolioRewards` and the rest without reading the source.
246
194
 
247
- Every method is fully typed, so your editor can discover the shape of responses
248
- (`PoolOverview`, `LockPeriod`, `PortfolioRewards`, etc.) without digging into
249
- the implementation.
195
+ ## Lite deployment behaviour
250
196
 
251
- ## Lite Deployment Behavior
197
+ With `isLiteDeployment: true`, KSU-related functionality degrades predictably
198
+ rather than throwing at random:
252
199
 
253
- When `isLiteDeployment: true` is set in the config, the SDK gracefully handles
254
- missing KSU-related functionality:
200
+ | Method | Full deployment | Lite deployment |
201
+ | ----------------------------------------------- | --------------------- | ------------------------------------ |
202
+ | `Locking.lockKSUTokens()` | Works normally | Throws |
203
+ | `Locking.getUserTotalLockedAmount()` | Returns locked amount | Returns `'0'` |
204
+ | `Locking.getLoyaltyLevelAndApyBonusFromRatio()` | Calculates the level | Returns level 0, 0% bonus |
205
+ | `Locking.getKasuEpochTokenPrice()` | Returns the price | Returns `{ price: 0, decimals: 18 }` |
206
+ | `Portfolio.getUserNfts()` | Returns NFT ids | Returns `[]` |
207
+ | `Portfolio.getPortfolioRewards()` | Returns rewards | Returns zeros |
255
208
 
256
- | Service Method | Full Deployment | Lite Deployment |
257
- |----------------|-----------------|-----------------|
258
- | `Locking.lockKSUTokens()` | Works normally | Throws error |
259
- | `Locking.getUserTotalLockedAmount()` | Returns locked amount | Returns `'0'` |
260
- | `Locking.getLoyaltyLevelAndApyBonusFromRatio()` | Calculates level | Returns level 0, 0% bonus |
261
- | `Locking.getKasuEpochTokenPrice()` | Returns current price | Returns `{ price: 0, decimals: 18 }` |
262
- | `Portfolio.getUserNfts()` | Returns NFT ids | Returns `[]` |
263
- | `Portfolio.getPortfolioRewards()` | Returns rewards | Returns zeros |
209
+ Applications should hide KSU surfaces the locking panel, loyalty badges, KSU
210
+ rewards — when `kasu.isLiteDeployment` is true.
264
211
 
265
- **UI Implications**: Frontends should hide KSU-related UI elements (locking panel,
266
- loyalty badges, KSU rewards) when `config.isLiteDeployment` is true.
212
+ ## Development
267
213
 
268
- ### Working with pool filters
269
-
270
- Many queries accept a list of pool ids to ignore (see `UNUSED_LENDING_POOL_IDS`
271
- in the config). In the Kasu frontend we load this list from Directus before
272
- creating the SDK:
273
-
274
- ```ts
275
- const unusedPools = await getUnusedPools(); // fetches Directus list
276
- const sdk = new KasuSdk(
277
- { ...config, UNUSED_LENDING_POOL_IDS: unusedPools.length ? unusedPools : [''] },
278
- provider,
279
- );
214
+ ```bash
215
+ npm ci
216
+ npm run build-tc # regenerate typechain factories from abis/
217
+ npm run build # eslint + tsc
218
+ npm run rollup-build # bundle to dist/
219
+ npm run test:unit # the offline suites — the fast loop, and what CI runs
220
+ npm test # the same suites; the live specs skip unless LIVE_TESTS=1
221
+ npm run test:live # opt-in: the specs that reach real subgraphs and RPCs
280
222
  ```
281
223
 
282
- Mirroring this pattern keeps your subgraph requests aligned with the official
283
- UI and prevents deprecated pools from leaking into calculations.
284
-
285
- ## Building & testing locally
224
+ Everything outside `src/tests/` is offline and fast. `src/tests/` holds the
225
+ specs that talk to live networks; they are skipped unless `LIVE_TESTS=1`, so a
226
+ network problem cannot fail a pull request that did not touch the network.
286
227
 
287
- ```bash
288
- npm install
289
- npm run build-tc # regenerate typechain factories
290
- npm run build # type-check + compile
291
- npm run rollup-build
292
- npm test
293
- ```
228
+ CI runs `build-tc`, `build`, `test:unit` and `rollup-build` on every pull
229
+ request.
294
230
 
295
- These are the same steps executed by the release workflow before publishing to
296
- npm.
231
+ ## Versioning & publishing
297
232
 
298
- ## Networks
233
+ The package follows semantic versioning. The published runtime entry is the
234
+ rollup bundle (`dist/bundle.cjs.js` / `dist/bundle.esm.js`), which
235
+ `prepublishOnly` rebuilds — so when verifying a release, check the contents of a
236
+ packed tarball rather than the version number alone.
299
237
 
300
- | Network | Chain ID | Deployment Type | Subgraph |
301
- |---------|----------|-----------------|----------|
302
- | Base | 8453 | Full | `kasu-base/v1` |
303
- | XDC | 50 | Lite | `kasu-xdc/v1.0.0` |
304
- | Plume | 98866 | Lite | `kasu-plume/prod` |
238
+ Publishing is manual and is done by a member of the npm organisation; the
239
+ tag workflow only verifies that a pushed `vX.Y.Z` tag matches `package.json`.
305
240
 
306
241
  ## Support
307
- For questions, issues, or contributions:
308
- - GitHub Issues: [Create an issue](https://github.com/kasufinance/kasu-sdk/issues)
309
- - Documentation: [View full docs](https://docs.kasu.finance)
310
- - Discord: [Join our community](https://discord.gg/kasufinance)
242
+
243
+ - Issues: <https://github.com/Kasu-Finance/kasu-sdk/issues>
244
+ - Developer documentation: <https://devdocs.kasu.finance>
245
+ - Product documentation: <https://docs.kasu.finance>
311
246
 
312
247
  ## License
313
- This project is licensed under the MIT License - see the LICENSE file for details.
248
+
249
+ MIT — see [LICENSE](./LICENSE).