@kasufinance/kasu-sdk 2.6.0 → 2.7.1

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 (126) hide show
  1. package/README.md +167 -4
  2. package/dist/bundle.cjs.js +1389 -257
  3. package/dist/bundle.esm.js +1375 -258
  4. package/dist/domain/index.d.ts +5 -2
  5. package/dist/domain/index.js +3 -2
  6. package/dist/domain/index.js.map +1 -1
  7. package/dist/domain/requests.d.ts +42 -4
  8. package/dist/domain/requests.js +20 -4
  9. package/dist/domain/requests.js.map +1 -1
  10. package/dist/domain/revert-errors.d.ts +59 -0
  11. package/dist/domain/revert-errors.js +190 -0
  12. package/dist/domain/revert-errors.js.map +1 -0
  13. package/dist/domain/wallet-errors.d.ts +63 -8
  14. package/dist/domain/wallet-errors.js +115 -16
  15. package/dist/domain/wallet-errors.js.map +1 -1
  16. package/dist/facade/deposits.js +2 -1
  17. package/dist/facade/deposits.js.map +1 -1
  18. package/dist/facade/flows.d.ts +95 -0
  19. package/dist/facade/flows.js +116 -0
  20. package/dist/facade/flows.js.map +1 -0
  21. package/dist/facade/index.d.ts +3 -0
  22. package/dist/facade/index.js +3 -0
  23. package/dist/facade/index.js.map +1 -1
  24. package/dist/facade/kasu.d.ts +6 -2
  25. package/dist/facade/kasu.js +28 -3
  26. package/dist/facade/kasu.js.map +1 -1
  27. package/dist/facade/read-only.d.ts +12 -0
  28. package/dist/facade/read-only.js +13 -0
  29. package/dist/facade/read-only.js.map +1 -0
  30. package/dist/flows/deposit-flow.d.ts +332 -0
  31. package/dist/flows/deposit-flow.js +436 -0
  32. package/dist/flows/deposit-flow.js.map +1 -0
  33. package/dist/flows/flow.d.ts +72 -0
  34. package/dist/flows/flow.js +110 -0
  35. package/dist/flows/flow.js.map +1 -0
  36. package/dist/flows/index.d.ts +19 -0
  37. package/dist/flows/index.js +17 -0
  38. package/dist/flows/index.js.map +1 -0
  39. package/dist/flows/observable.d.ts +59 -0
  40. package/dist/flows/observable.js +81 -0
  41. package/dist/flows/observable.js.map +1 -0
  42. package/dist/flows/withdraw-flow.d.ts +99 -0
  43. package/dist/flows/withdraw-flow.js +83 -0
  44. package/dist/flows/withdraw-flow.js.map +1 -0
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.js +6 -0
  47. package/dist/index.js.map +1 -1
  48. package/dist/services/DataService/data-service.js +0 -1
  49. package/dist/services/DataService/data-service.js.map +1 -1
  50. package/package.json +8 -2
  51. package/src/domain/index.ts +10 -1
  52. package/src/domain/requests.ts +57 -4
  53. package/src/domain/revert-errors.ts +248 -0
  54. package/src/domain/wallet-errors.ts +135 -18
  55. package/src/facade/deposits.ts +2 -3
  56. package/src/facade/flows.ts +172 -0
  57. package/src/facade/index.ts +8 -0
  58. package/src/facade/kasu.ts +43 -3
  59. package/src/facade/read-only.ts +13 -0
  60. package/src/flows/deposit-flow.ts +884 -0
  61. package/src/flows/flow.ts +108 -0
  62. package/src/flows/index.ts +47 -0
  63. package/src/flows/observable.ts +97 -0
  64. package/src/flows/withdraw-flow.ts +210 -0
  65. package/src/index.ts +7 -0
  66. package/src/services/DataService/data-service.ts +0 -1
  67. package/dist/domain/au-minimum.test.d.ts +0 -1
  68. package/dist/domain/au-minimum.test.js +0 -202
  69. package/dist/domain/au-minimum.test.js.map +0 -1
  70. package/dist/domain/deposit-bounds.test.d.ts +0 -1
  71. package/dist/domain/deposit-bounds.test.js +0 -135
  72. package/dist/domain/deposit-bounds.test.js.map +0 -1
  73. package/dist/domain/loan-contract.test.d.ts +0 -1
  74. package/dist/domain/loan-contract.test.js +0 -255
  75. package/dist/domain/loan-contract.test.js.map +0 -1
  76. package/dist/domain/partners.test.d.ts +0 -1
  77. package/dist/domain/partners.test.js +0 -53
  78. package/dist/domain/partners.test.js.map +0 -1
  79. package/dist/domain/pools.test.d.ts +0 -1
  80. package/dist/domain/pools.test.js +0 -184
  81. package/dist/domain/pools.test.js.map +0 -1
  82. package/dist/domain/rates.test.d.ts +0 -1
  83. package/dist/domain/rates.test.js +0 -181
  84. package/dist/domain/rates.test.js.map +0 -1
  85. package/dist/domain/requests.test.d.ts +0 -1
  86. package/dist/domain/requests.test.js +0 -470
  87. package/dist/domain/requests.test.js.map +0 -1
  88. package/dist/domain/settlement.test.d.ts +0 -1
  89. package/dist/domain/settlement.test.js +0 -152
  90. package/dist/domain/settlement.test.js.map +0 -1
  91. package/dist/domain/tranche-display-name.test.d.ts +0 -1
  92. package/dist/domain/tranche-display-name.test.js +0 -58
  93. package/dist/domain/tranche-display-name.test.js.map +0 -1
  94. package/dist/domain/tranches.test.d.ts +0 -1
  95. package/dist/domain/tranches.test.js +0 -206
  96. package/dist/domain/tranches.test.js.map +0 -1
  97. package/dist/domain/wallet-errors.test.d.ts +0 -1
  98. package/dist/domain/wallet-errors.test.js +0 -71
  99. package/dist/domain/wallet-errors.test.js.map +0 -1
  100. package/dist/facade/config.test.d.ts +0 -1
  101. package/dist/facade/config.test.js +0 -216
  102. package/dist/facade/config.test.js.map +0 -1
  103. package/dist/facade/facade.test.d.ts +0 -1
  104. package/dist/facade/facade.test.js +0 -278
  105. package/dist/facade/facade.test.js.map +0 -1
  106. package/dist/services/Locking/calculate-apy.test.d.ts +0 -1
  107. package/dist/services/Locking/calculate-apy.test.js +0 -41
  108. package/dist/services/Locking/calculate-apy.test.js.map +0 -1
  109. package/dist/tests/sample.test.d.ts +0 -1
  110. package/dist/tests/sample.test.js +0 -59
  111. package/dist/tests/sample.test.js.map +0 -1
  112. package/src/domain/au-minimum.test.ts +0 -371
  113. package/src/domain/deposit-bounds.test.ts +0 -200
  114. package/src/domain/loan-contract.test.ts +0 -343
  115. package/src/domain/partners.test.ts +0 -83
  116. package/src/domain/pools.test.ts +0 -260
  117. package/src/domain/rates.test.ts +0 -254
  118. package/src/domain/requests.test.ts +0 -653
  119. package/src/domain/settlement.test.ts +0 -198
  120. package/src/domain/tranche-display-name.test.ts +0 -96
  121. package/src/domain/tranches.test.ts +0 -300
  122. package/src/domain/wallet-errors.test.ts +0 -100
  123. package/src/facade/config.test.ts +0 -265
  124. package/src/facade/facade.test.ts +0 -374
  125. package/src/services/Locking/calculate-apy.test.ts +0 -52
  126. package/src/tests/sample.test.ts +0 -63
package/README.md CHANGED
@@ -8,9 +8,10 @@
8
8
  real-world credit funded on-chain. It wraps the core contracts, the subgraphs
9
9
  and the CMS behind one object, so an application can list lending strategies,
10
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,
11
+ re-implementing the plumbing. It also ships a pure domain layer — the rate,
12
12
  tranche and pool rules every Kasu frontend agrees on, as numbers rather than
13
- copy.
13
+ copy — and headless deposit and withdrawal flows that own the ORDER of those
14
+ transactions so no application has to.
14
15
 
15
16
  ## Installation
16
17
 
@@ -117,6 +118,7 @@ You can also pass a whole `ChainConfigEntry` instead of a chain key.
117
118
  | `kasu.strategies` | `getAll()`, `getVisible()`, `getById()`, `getPlatformStats()`, `getPerformanceFeePercent()`, `calculateDepositLimits()` | Browse pools, APY, capacity |
118
119
  | `kasu.deposits` | `deposit()`, `withdraw()`, `withdrawMax()`, `buildKycParams()`, `isClearingPending()` | Submit transactions |
119
120
  | `kasu.portfolio` | `getPositions()`, `getTransactionHistory()`, `getRequestStates()` | Lender balances, yield, history |
121
+ | `kasu.flows` | `deposit(ports)`, `withdraw(ports?)` | Headless transaction pipelines |
120
122
 
121
123
  On the instance itself: `kasu.connect(signer)`, `kasu.isReadOnly`,
122
124
  `kasu.provider`, `kasu.chainConfig`, `kasu.isLiteDeployment`, and
@@ -145,7 +147,8 @@ application formats them in its own design system and language.
145
147
  | Requests | `deriveRequestState`, `submissionEvents`, `countSubmissions`, `firstSubmissionTimestamp`, `isCycleClosed` |
146
148
  | Settlement | `computeSettlementWindow`, `nextCycleBoundary`, `deriveCycleDates`, `CLEARING_WINDOW_SECONDS` |
147
149
  | Loan contract | `encodeDepositData`, `buildContractVersionType`, `buildLoanAgreementSignMessage`, `buildLegacyContractRequestMessage`, `buildFullNameRequestMessage`, `formatSignTimestampUtc`, `parseFormattedMessage`, `asContractType` |
148
- | Wallet errors | `isUserRejected`, `isUnpredictableGas` |
150
+ | Wallet errors | `isUserRejected`, `isUnpredictableGas`, `classifyWalletFailure` |
151
+ | Revert errors | `decodeRevert`, `extractRevertData` — the bytes a reverted call came back with, read as the custom error the contract named |
149
152
  | AU minimum | `auMinimumRemaining`, `isAustralianKyc`, `auThresholdFor`, `isAuMinimumExempt`, `parseMinorUnits`, `AU_ALPHA3`, `AU_MIN_CUMULATIVE_BY_STABLE` |
150
153
 
151
154
  Three rules worth knowing:
@@ -165,7 +168,167 @@ Three rules worth knowing:
165
168
  `Senior` tranche to "Upper Mezzanine" on Apxium strategies, because the true
166
169
  senior position is held by an institutional lender. Call it at the view
167
170
  boundary only: ranking, matching and sorting keep the raw on-chain name —
168
- which is why `deriveRequestState` returns `trancheName` raw.
171
+ which is why `deriveRequestState` returns `trancheName` raw — and
172
+ `reallocationTargetTrancheName` with it.
173
+
174
+ ## Flows
175
+
176
+ `DepositFlow` and `WithdrawFlow` are the domain layer one level up: a rule that
177
+ unfolds over TIME. They own the order of a deposit and a withdrawal, the guards
178
+ around them, and the codes that describe where a run got to. They own no UI, no
179
+ framework, no network and **no copy** — every observable state is a code, and
180
+ your application maps it to its own words in its own language.
181
+
182
+ Every side effect is an injected port, so the SDK never learns a URL, a key or
183
+ a wallet. `kasu.flows.deposit()` fills the ports it can from this instance; the
184
+ three that reach your own backend or wallet you always supply.
185
+
186
+ ```ts
187
+ import { Kasu } from '@kasufinance/kasu-sdk';
188
+ import { parseUnits } from 'ethers/lib/utils';
189
+
190
+ const kasu = Kasu.create({ chain: 'base' }).connect(signer);
191
+
192
+ const flow = kasu.flows.deposit({
193
+ signMessage: (message) => signer.signMessage(message),
194
+ generateContract: (req) => postJson('/api/agreements/generate', req),
195
+ getKycSignature: (params) => postJson('/api/kyc-signature', params),
196
+ // readAllowance, approve, deposit and buildKycParams default to the SDK's
197
+ // own signer-bound implementations — override any of them if you need to.
198
+ });
199
+
200
+ const stop = flow.subscribe((state) => {
201
+ // Every transition. `state.phase`, `state.step`, `state.stepIndex`,
202
+ // `state.stepTotal`, `state.contract`, `state.failure` — all codes.
203
+ render(state);
204
+ });
205
+
206
+ await flow.start({
207
+ poolId,
208
+ trancheId,
209
+ amount: parseUnits('1000', 6), // BASE units
210
+ fixedTermConfigId: '0', // '0' = variable rate
211
+ userAddress,
212
+ depositAmount: 1000, // display units, for the backend's integrity check
213
+ contractMessage: {
214
+ format: 'loan-agreement',
215
+ strategyName,
216
+ region,
217
+ optionName,
218
+ amountLabel, // built from the SAME value as depositAmount
219
+ },
220
+ });
221
+
222
+ // The run PARKS on `awaiting-accept` with the agreement in `state.contract`.
223
+ // Show it, then settle the handshake:
224
+ await flow.acceptContract(); // signs it and carries on
225
+ // or: flow.declineContract(); → phase 'declined', nothing submitted
226
+ ```
227
+
228
+ **Phases** — `idle`, `generating-sign`, `generating-fetch`, `awaiting-accept`,
229
+ `accepting-sign`, `approve`, `request-sign`, `request-confirm`, and the
230
+ terminal `success` / `declined` / `error`. Each maps to one of four steps
231
+ (`generate`, `confirm`, `approve`, `request`); `stepIndex` and `stepTotal` are
232
+ published because the approve step drops out of the sequence when the allowance
233
+ already covers the deposit, and a badge total that changed under a lender would
234
+ be describing a pipeline they are not in.
235
+
236
+ **Failures** are codes, never sentences:
237
+
238
+ ```ts
239
+ type DepositFailure =
240
+ | { step: DepositStep; reason: 'cancelled' } // isUserRejected
241
+ | { step: DepositStep; reason: 'failed'; error: unknown }
242
+ | { step: 'request'; reason: 'insufficient-balance'; error: unknown }
243
+ | {
244
+ step: 'request';
245
+ reason: 'reverted';
246
+ revertError: string; // e.g. 'ClearingIsPending'
247
+ error: unknown;
248
+ }
249
+ | { step: 'request'; reason: 'contract-expired' }; // the 5-minute TTL
250
+ ```
251
+
252
+ A wallet rejection is `cancelled`, not a failure: the lender changed their
253
+ mind, and telling them something broke would be a lie. Everything else is
254
+ `failed` and carries the original error for your crash reporter.
255
+
256
+ A revert on the request step is read for WHAT reverted. `requestDepositWithKyc`
257
+ reverts for a family of reasons the ABI declares — `LendingPoolIsStopped`,
258
+ `ClearingIsPending`, `InvalidTranche`, `UserNotKycd`, `UserBlocked`,
259
+ `UserNotInAllowList`, `BlockExpired` — and none of them is a shortfall. Those
260
+ come back as `reverted`, with `revertError` naming the contract error. Only a
261
+ token balance or allowance failure, or revert data nothing can decode, is
262
+ `insufficient-balance`.
263
+
264
+ **Render `reverted` with generic copy and special-case only the names you have
265
+ words for.** A contract upgrade can add an error, so treat the set as open: a
266
+ consumer that assumed it was closed has nothing to show for a new one.
267
+ `decodeRevert(err)` is exported if you want the same reading elsewhere.
268
+
269
+ ```ts
270
+ if (failure.reason === 'reverted') {
271
+ switch (failure.revertError) {
272
+ case 'ClearingIsPending':
273
+ return t('deposit.error.clearing');
274
+ case 'UserNotKycd':
275
+ return t('deposit.error.kyc');
276
+ default:
277
+ return t('deposit.error.rejectedOnChain'); // generic
278
+ }
279
+ }
280
+ ```
281
+
282
+ **Both personal signs are length-checked where they are taken.** A wallet is
283
+ not obliged to return 65 bytes and the ABI coder will not catch one that does
284
+ not — `0x` encodes into `depositData` as happily as a real signature does, and
285
+ the agreements service could then never verify the deposit. A malformed auth
286
+ signature fails the `generate` step and a malformed acceptance signature fails
287
+ `confirm`, both as `failed`, before anything is broadcast.
288
+
289
+ Three guarantees worth knowing:
290
+
291
+ - **The approve is for the EXACT amount, never unlimited.** An unlimited
292
+ allowance outlives the deposit it was granted for; a later exploit of the
293
+ spender would drain a wallet that stopped lending months ago.
294
+ - **The allowance is read live, before anything is signed.** An exact-amount
295
+ approval is fully consumed by the deposit it paid for, so a cached allowance
296
+ is exactly the value that wrongly skips the approve and reverts the deposit.
297
+ - **The spender is the SDK's, not yours.** `kasu.flows.deposit()` defaults it
298
+ to this chain's `LendingPoolManager` — the only contract the default deposit
299
+ port calls — so you no longer pass it. `spender` on the input still
300
+ overrides, for a consumer that replaced the `deposit` port; a wrong one is an
301
+ approval granted to the wrong contract and then a revert you would read as
302
+ `insufficient-balance`.
303
+ - **`reset()` is safe mid-flight, and immediate.** It abandons the run, drops
304
+ its remaining transitions, unparks a waiting handshake and releases the
305
+ re-entry guard in the SAME tick — so `flow.reset(); flow.start(next)` starts
306
+ the next run even when the abandoned one is still parked on a wallet prompt
307
+ that will never answer. An abandoned run can never drive a view you have left
308
+ back to `success`. `start()` is otherwise guarded against re-entry, so a
309
+ double tap cannot fire two deposits.
310
+ - **Only wallet errors can be `cancelled`.** The ports that reach your backend
311
+ — `generateContract`, `buildKycParams`, `getKycSignature` — always fail as
312
+ `failed`, with the error kept. A backend that words a refusal "declined" is
313
+ never reported to a lender as something they did in their wallet.
314
+
315
+ `WithdrawFlow` is the same shape and much smaller — an optional KYC pre-check,
316
+ then `withdraw` or `withdrawMax`, with the same `cancelled` / `failed` split:
317
+
318
+ ```ts
319
+ const flow = kasu.flows.withdraw();
320
+ await flow.start({ poolId, trancheId, amount: 'max', userAddress });
321
+
322
+ // With the pre-check: supply `getKycSignature` and the run checks the lender's
323
+ // KYC before opening the wallet, on a `checking-kyc` phase and a `kyc` step.
324
+ // `buildKycParams` defaults to the SDK's, exactly as on the deposit path.
325
+ const checked = kasu.flows.withdraw({
326
+ getKycSignature: (params) => postJson('/api/kyc-signature', params),
327
+ });
328
+ ```
329
+
330
+ `'max'` is a code you pass, not a balance you read: it routes to the all-shares
331
+ call, which resolves the balance on chain at execution.
169
332
 
170
333
  ## Low-level `KasuSdk`
171
334