@kasufinance/kasu-sdk 2.8.0 → 2.10.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 (88) hide show
  1. package/README.md +120 -9
  2. package/dist/bundle.cjs.js +2068 -478
  3. package/dist/bundle.esm.js +2030 -480
  4. package/dist/domain/batch-proof.d.ts +210 -0
  5. package/dist/domain/batch-proof.js +384 -0
  6. package/dist/domain/batch-proof.js.map +1 -0
  7. package/dist/domain/explorer-urls.d.ts +33 -0
  8. package/dist/domain/explorer-urls.js +38 -0
  9. package/dist/domain/explorer-urls.js.map +1 -0
  10. package/dist/domain/fixed-term.d.ts +74 -0
  11. package/dist/domain/fixed-term.js +86 -0
  12. package/dist/domain/fixed-term.js.map +1 -0
  13. package/dist/domain/index.d.ts +12 -2
  14. package/dist/domain/index.js +7 -2
  15. package/dist/domain/index.js.map +1 -1
  16. package/dist/domain/partners.d.ts +29 -0
  17. package/dist/domain/partners.js +20 -0
  18. package/dist/domain/partners.js.map +1 -1
  19. package/dist/domain/requests.d.ts +24 -0
  20. package/dist/domain/requests.js +33 -0
  21. package/dist/domain/requests.js.map +1 -1
  22. package/dist/domain/sponsored-calls.d.ts +74 -0
  23. package/dist/domain/sponsored-calls.js +156 -0
  24. package/dist/domain/sponsored-calls.js.map +1 -0
  25. package/dist/domain/yields.d.ts +121 -0
  26. package/dist/domain/yields.js +151 -0
  27. package/dist/domain/yields.js.map +1 -0
  28. package/dist/facade/calls.d.ts +59 -0
  29. package/dist/facade/calls.js +89 -0
  30. package/dist/facade/calls.js.map +1 -0
  31. package/dist/facade/chain-configs.js +5 -0
  32. package/dist/facade/chain-configs.js.map +1 -1
  33. package/dist/facade/flows.d.ts +24 -3
  34. package/dist/facade/flows.js +22 -2
  35. package/dist/facade/flows.js.map +1 -1
  36. package/dist/facade/index.d.ts +3 -1
  37. package/dist/facade/index.js +1 -0
  38. package/dist/facade/index.js.map +1 -1
  39. package/dist/facade/kasu.d.ts +8 -2
  40. package/dist/facade/kasu.js +17 -5
  41. package/dist/facade/kasu.js.map +1 -1
  42. package/dist/facade/strategies.d.ts +43 -2
  43. package/dist/facade/strategies.js +80 -4
  44. package/dist/facade/strategies.js.map +1 -1
  45. package/dist/facade/types.d.ts +18 -0
  46. package/dist/flows/batch-errors.d.ts +74 -0
  47. package/dist/flows/batch-errors.js +83 -0
  48. package/dist/flows/batch-errors.js.map +1 -0
  49. package/dist/flows/batch-submission.d.ts +223 -0
  50. package/dist/flows/batch-submission.js +321 -0
  51. package/dist/flows/batch-submission.js.map +1 -0
  52. package/dist/flows/deposit-flow.d.ts +13 -20
  53. package/dist/flows/deposit-flow.js +14 -25
  54. package/dist/flows/deposit-flow.js.map +1 -1
  55. package/dist/flows/index.d.ts +6 -2
  56. package/dist/flows/index.js +4 -2
  57. package/dist/flows/index.js.map +1 -1
  58. package/dist/flows/withdraw-flow.d.ts +95 -4
  59. package/dist/flows/withdraw-flow.js +107 -2
  60. package/dist/flows/withdraw-flow.js.map +1 -1
  61. package/dist/services/DataService/queries.js +1 -0
  62. package/dist/services/DataService/queries.js.map +1 -1
  63. package/dist/services/UserLending/user-lending.d.ts +13 -0
  64. package/dist/services/UserLending/user-lending.js +25 -4
  65. package/dist/services/UserLending/user-lending.js.map +1 -1
  66. package/package.json +1 -1
  67. package/src/domain/batch-proof.ts +576 -0
  68. package/src/domain/explorer-urls.ts +74 -0
  69. package/src/domain/fixed-term.ts +115 -0
  70. package/src/domain/index.ts +56 -0
  71. package/src/domain/partners.ts +35 -0
  72. package/src/domain/requests.ts +37 -0
  73. package/src/domain/sponsored-calls.ts +213 -0
  74. package/src/domain/yields.ts +189 -0
  75. package/src/facade/calls.ts +135 -0
  76. package/src/facade/chain-configs.ts +5 -0
  77. package/src/facade/flows.ts +23 -2
  78. package/src/facade/index.ts +3 -0
  79. package/src/facade/kasu.ts +31 -2
  80. package/src/facade/strategies.ts +98 -7
  81. package/src/facade/types.ts +19 -0
  82. package/src/flows/batch-errors.ts +118 -0
  83. package/src/flows/batch-submission.ts +511 -0
  84. package/src/flows/deposit-flow.ts +27 -33
  85. package/src/flows/index.ts +33 -2
  86. package/src/flows/withdraw-flow.ts +219 -10
  87. package/src/services/DataService/queries.ts +1 -0
  88. package/src/services/UserLending/user-lending.ts +38 -10
package/README.md CHANGED
@@ -100,13 +100,14 @@ a read-only `Kasu.create({ chain: 'plume' })` throws — pass your own provider
100
100
  read its history.
101
101
 
102
102
  Every entry lives in `CHAIN_CONFIGS`, including contract addresses, subgraph
103
- URLs, the stable asset and public RPC defaults:
103
+ URLs, the stable asset, public RPC defaults and the block explorer:
104
104
 
105
105
  ```ts
106
- import { CHAIN_CONFIGS } from '@kasufinance/kasu-sdk';
106
+ import { CHAIN_CONFIGS, explorerTxUrl } from '@kasufinance/kasu-sdk';
107
107
 
108
108
  CHAIN_CONFIGS.base.stableAsset; // { address, symbol, name, decimals, currencyCode }
109
109
  CHAIN_CONFIGS.xdc.rpcUrls; // starting preference only — override with your own
110
+ explorerTxUrl(CHAIN_CONFIGS.base, txHash); // https://basescan.org/tx/0x… — throws on a malformed hash
110
111
  ```
111
112
 
112
113
  You can also pass a whole `ChainConfigEntry` instead of a chain key.
@@ -115,10 +116,11 @@ You can also pass a whole `ChainConfigEntry` instead of a chain key.
115
116
 
116
117
  | Facade | Methods | Purpose |
117
118
  | ----------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
118
- | `kasu.strategies` | `getAll()`, `getVisible()`, `getById()`, `getPlatformStats()`, `getPerformanceFeePercent()`, `calculateDepositLimits()` | Browse pools, APY, capacity |
119
+ | `kasu.strategies` | `getAll()`, `getVisible()`, `getById()`, `getPlatformStats()`, `getPerformanceFeePercent()`, `calculateDepositLimits()`, `getRiskPerformance(poolId)`, `getOriginator(poolId)` | Browse pools, APY, capacity, risk, originator |
119
120
  | `kasu.deposits` | `deposit()`, `withdraw()`, `withdrawMax()`, `buildKycParams()`, `isClearingPending()` | Submit transactions |
120
121
  | `kasu.portfolio` | `getPositions()`, `getTransactionHistory()`, `getRequestStates()` | Lender balances, yield, history |
121
122
  | `kasu.flows` | `deposit(ports)`, `withdraw(ports?)` | Headless transaction pipelines |
123
+ | `kasu.calls` | `requestWithdrawal(params)`, `cancelDepositRequest(poolId, dNftId)`, `cancelWithdrawalRequest(poolId, wNftId)`, `withdrawAtExpiry(poolId, lockId)` | Encoded calls for a sponsored relay (read, then encode — never sends) |
122
124
 
123
125
  On the instance itself: `kasu.connect(signer)`, `kasu.isReadOnly`,
124
126
  `kasu.provider`, `kasu.chainConfig`, `kasu.isLiteDeployment`, and
@@ -142,7 +144,10 @@ application formats them in its own design system and language.
142
144
  | Tranches | `trancheHasCapacity`, `poolAllTranchesFull`, `derivePoolStatus`, `trancheRiskRank`, `compareTrancheSeniority`, `pickDefaultTrancheId`, `trancheApyBounds`, `netTrancheApyBounds`, `MIN_TRANCHE_CAPACITY` |
143
145
  | Deposit bounds | `resolveDepositBounds`, `resolveBoundShortcuts`, `isBelowMinimumCapacity`, `parseTrancheBound`, `floorToCents`, `ceilToCents` |
144
146
  | Pools | `selectVisiblePools`, `poolMaxApy`, `pickHighestYieldTranche`, `maxNetRateCeiling` |
145
- | Partners | `getCreditOriginator`, `getInstitutionalLender`, `APXIUM`, `INVOICEMATE`, `RIXON_CAPITAL` |
147
+ | Partners | `getCreditOriginator`, `getInstitutionalLender`, `getStrategyRegion`, `APXIUM`, `INVOICEMATE`, `RIXON_CAPITAL` (each with a `region`) |
148
+ | Fixed term | `availableFixedTermConfigs`, `isFixedTermConfigId`, `lockDurationDays`, `lockDurationMonths`, `DAYS_PER_EPOCH`, `DAYS_PER_MONTH`, `MIN_DAYS_FOR_MONTHS` |
149
+ | Yields | `weeklyInterest`, `dailyInterest`, `accruedInterest`, `projectedAnnualEarnings`, `weightedAverageApy`, `SECONDS_PER_WEEK` — NET of the performance fee |
150
+ | Batch proof | `validateDepositBatchCalls`, `proveDepositInReceipt`, `DepositBatchMismatchError`, `ENTRY_POINT_V07` |
146
151
  | Tranche display name | `getTrancheDisplayName`, `UPPER_MEZZANINE` |
147
152
  | Requests | `deriveRequestState`, `submissionEvents`, `countSubmissions`, `firstSubmissionTimestamp`, `isCycleClosed` |
148
153
  | Settlement | `computeSettlementWindow`, `nextCycleBoundary`, `deriveCycleDates`, `CLEARING_WINDOW_SECONDS` |
@@ -264,9 +269,60 @@ flow.authoriseTransfer(); // the lender's explicit go-ahead
264
269
  - `state.batched` is `true` from the first state of a batched run. It never
265
270
  enters `approve`, so it counts as a three-step run (`stepTotal` 3).
266
271
  - Throw `BatchRevertedError(revertError)` when the batch is known to revert →
267
- `{ step: 'request', reason: 'reverted', revertError }`. Any other throw is
268
- read like a direct deposit's: a wallet rejection is `cancelled`, everything
269
- else `failed`.
272
+ `{ step: 'request', reason: 'reverted', revertError }`. Throw a
273
+ `BatchSubmissionError` (or `BatchPendingError`) to report a submission
274
+ outcome: `pending` → `{ reason: 'pending' }`, `already-submitted` →
275
+ `{ reason: 'already-submitted' }`, `failed` → `{ reason: 'failed' }` — read
276
+ at its word, never through the revert decoder. Any other throw is read like a
277
+ direct deposit's: a wallet rejection is `cancelled`, everything else
278
+ `failed`.
279
+
280
+ ### Submitting a sponsored batch: `submitBatch` (2.9.0)
281
+
282
+ For a submitter that relays the batch and is asked for its status later (a
283
+ sponsored-gas relay, for one), `submitBatch` owns the polling policy, the
284
+ receipt proof and the outcome taxonomy. Your ports do the I/O; the SDK decides
285
+ what each answer means:
286
+
287
+ ```ts
288
+ import {
289
+ submitBatch,
290
+ validateDepositBatchCalls,
291
+ } from '@kasufinance/kasu-sdk';
292
+
293
+ depositBatch: (calls) => {
294
+ // Before signing: the batch is exactly this run's deposit, or it throws.
295
+ const { tranche, amount } = validateDepositBatchCalls(calls, {
296
+ stableAsset, spender, poolId, trancheId, amount, fixedTermConfigId,
297
+ });
298
+ return submitBatch(
299
+ // `pendingPool` is the strategy's pending pool (`ILendingPool.pendingPool()`):
300
+ // with it, only THAT contract's `DepositRequested` can prove the deposit.
301
+ { calls, expect: { lender, tranche, amount, pendingPool } },
302
+ {
303
+ post: (calls) => postToMyRelay(calls), // → { transactionId }
304
+ getStatus: (id) => readMyRelayStatus(id), // normalised BatchStatusRead
305
+ getReceipt: (hash) => provider.waitForTransaction(hash, 1, 120_000),
306
+ now: Date.now,
307
+ sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
308
+ },
309
+ );
310
+ },
311
+ ```
312
+
313
+ - **An ambiguous result is never reported as failed.** Once the batch has been
314
+ posted only a revert proves nothing was deposited; a timeout, an unreadable
315
+ receipt, an unknown status or an unrecognised `post` error is a
316
+ `BatchPendingError`, and the run ends `{ reason: 'pending' }`. Tell the
317
+ lender it may still be processing — never "try again".
318
+ - `wait()` resolves only when `proveDepositInReceipt` finds THIS lender's
319
+ `DepositRequested` for the expected tranche AND amount inside their own user
320
+ operation — and, when `pendingPool` is given, emitted by that contract. A
321
+ handleOps receipt's `status` decides nothing.
322
+ - Polling: first read at once, then 3 s ×1.5 to a 10 s ceiling, for 10 minutes
323
+ (`DEFAULT_BATCH_POLLING_POLICY`; pass a partial policy to override).
324
+ - `checkBatchOutcome(transactionId, expect, ports)` is the one-read check for an
325
+ app's guard against lending twice after a `pending` run.
270
326
 
271
327
  **Phases** — `idle`, `generating-sign`, `generating-fetch`, `awaiting-accept`,
272
328
  `accepting-sign`, `approve`, `awaiting-authorise` (batch path only),
@@ -290,7 +346,12 @@ type DepositFailure =
290
346
  revertError: string; // e.g. 'ClearingIsPending'
291
347
  error: unknown;
292
348
  }
293
- | { step: 'request'; reason: 'contract-expired' }; // the 5-minute TTL
349
+ | { step: 'request'; reason: 'contract-expired' } // the 5-minute TTL
350
+ | {
351
+ step: 'request';
352
+ reason: 'pending' | 'already-submitted'; // batch path only — NOT failures
353
+ error: unknown;
354
+ };
294
355
  ```
295
356
 
296
357
  A wallet rejection is `cancelled`, not a failure: the lender changed their
@@ -372,7 +433,57 @@ const checked = kasu.flows.withdraw({
372
433
  ```
373
434
 
374
435
  `'max'` is a code you pass, not a balance you read: it routes to the all-shares
375
- call, which resolves the balance on chain at execution.
436
+ request, whose share count the SDK reads (`balanceOf`) at submission instead of
437
+ converting an asset figure — so no dust is stranded.
438
+
439
+ ### Sponsored withdraw and cancel (2.10.0)
440
+
441
+ `kasu.calls` encodes the single `LendingPoolManager` calls a gas-sponsoring
442
+ relay carries. Each returns `{ kind, call }`: post `kind` and `[call]` exactly
443
+ as given — the relay judges the call by its kind, so never map one yourself.
444
+
445
+ ```ts
446
+ import { submitSponsoredCall } from '@kasufinance/kasu-sdk';
447
+
448
+ const ports = {
449
+ post: ({ kind, call }) => postToMyRelay({ kind, calls: [call] }), // → { transactionId }
450
+ getStatus: (id) => readMyRelayStatus(id), // normalised BatchStatusRead
451
+ getReceipt: (hash) => provider.waitForTransaction(hash, 1, 120_000),
452
+ now: Date.now,
453
+ sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
454
+ };
455
+
456
+ // Withdraw: the flow encodes the call (shares from convertToShares, or the
457
+ // whole balanceOf for 'max') and submits it through your port.
458
+ const flow = kasu.flows.withdraw({
459
+ submitCall: (call) =>
460
+ submitSponsoredCall({ call, expect: { sender: lender } }, ports),
461
+ });
462
+ flow.state.sponsored; // true, from the first state
463
+ await flow.start({ poolId, trancheId, amount: 'max', userAddress: lender });
464
+
465
+ // Cancel: encode, then submit the same way.
466
+ const cancel = kasu.calls.cancelDepositRequest(poolId, dNftId);
467
+ const submission = await submitSponsoredCall(
468
+ { call: cancel, expect: { sender: lender } },
469
+ ports,
470
+ );
471
+ await submission.wait(); // resolves only when the lender's operation executed
472
+ ```
473
+
474
+ - `submitSponsoredCall` is `submitBatch` for one call: the same polling policy
475
+ and outcome table, with `proveUserOperationInReceipt` as the proof — the
476
+ lender's own operation, from the EntryPoint, with `success` set. A reverted
477
+ operation is `BatchRevertedError`; anything unproven is `BatchPendingError`.
478
+ - On the sponsored path a withdrawal's failure can also be `{ reason:
479
+ 'reverted', revertError }` and `{ reason: 'pending' | 'already-submitted' }`.
480
+ A wallet rejection thrown by `submitCall` (the lender declined to sign) is
481
+ `cancelled`. Any other throw from `submitCall` or `wait()` the SDK does not
482
+ recognise is `pending` — it may have left, so never tell the lender to try
483
+ again. A throw while encoding (a share read, an empty position) is `failed`:
484
+ nothing was sent.
485
+ - `cancelDeadline(requestState, nextEpochStart, nowSeconds)` is the "cancel
486
+ until" instant for a pending request (the next cycle close), or `null`.
376
487
 
377
488
  ## Low-level `KasuSdk`
378
489