@kasufinance/kasu-sdk 2.10.2 → 2.11.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.
@@ -1,4 +1,5 @@
1
1
  import { selectVisiblePools } from '../domain/pools';
2
+ import { pickShortDescription } from '../domain/strategy-summary';
2
3
  import { DataService } from '../services/DataService/data-service';
3
4
  import {
4
5
  PoolDelegateProfileAndHistory,
@@ -215,7 +216,10 @@ export class StrategiesFacade {
215
216
  return {
216
217
  id: pool.id,
217
218
  name: pool.poolName,
219
+ subheading: nonEmptyOrUndefined(pool.subheading),
218
220
  description: pool.description,
221
+ liteDescription: nonEmptyOrUndefined(pool.liteDescription),
222
+ summary: pickShortDescription(pool),
219
223
  isActive: pool.isActive,
220
224
  apy: pool.apy,
221
225
  tvl: pool.totalValueLocked,
@@ -231,6 +235,17 @@ export class StrategiesFacade {
231
235
  }
232
236
  }
233
237
 
238
+ /**
239
+ * A CMS text field as a string, or `undefined` when it is absent, not a
240
+ * string, or empty (blank included) — the CMS stores an emptied field as `''`, and an empty
241
+ * subheading must read as "none", not render an empty line.
242
+ */
243
+ function nonEmptyOrUndefined(value: unknown): string | undefined {
244
+ return typeof value === 'string' && value.trim().length > 0
245
+ ? value
246
+ : undefined;
247
+ }
248
+
234
249
  /**
235
250
  * A CMS "numeric" field as a finite number, or `null`. An empty or blank
236
251
  * string is UNAUTHORED, not zero.
@@ -1,7 +1,11 @@
1
1
  import { BigNumberish, BytesLike } from 'ethers';
2
2
 
3
3
  import { ContractAddresses, SdkConfigOptions } from '../sdk-config';
4
- import { LendingTotals, PoolOverview, TrancheData } from '../services/DataService/types';
4
+ import {
5
+ LendingTotals,
6
+ PoolOverview,
7
+ TrancheData,
8
+ } from '../services/DataService/types';
5
9
  import {
6
10
  PortfolioLendingPool,
7
11
  PortfolioSummary,
@@ -106,7 +110,30 @@ export interface KasuOptions {
106
110
  export interface Strategy {
107
111
  id: string;
108
112
  name: string;
113
+ /**
114
+ * The CMS subheading (the borrower type shown under the name). Absent when
115
+ * the record has none, or an empty one.
116
+ */
117
+ subheading?: string;
109
118
  description: string;
119
+ /**
120
+ * The CMS short description (`liteDescription`) for compact surfaces.
121
+ * Absent when the record has none, or an empty one.
122
+ */
123
+ liteDescription?: string;
124
+ /**
125
+ * The strategy's short blurb as ONE line of plain text, for cards and
126
+ * compact surfaces: `liteDescription` when it is usable, else the first
127
+ * paragraph of `description`, with inline markdown markers stripped and
128
+ * every run of whitespace collapsed to one space. The `"Not Available"`
129
+ * placeholder, an empty or blank value, and a missing one all count as
130
+ * absent. `undefined` when neither field yields a blurb; the SDK never
131
+ * substitutes a sentence of its own. See `pickShortDescription`.
132
+ *
133
+ * Not sanitised: HTML tags and link syntax pass through unchanged. Render
134
+ * it as text, never as HTML or markdown.
135
+ */
136
+ summary?: string;
110
137
  isActive: boolean;
111
138
  /** Weighted-average APY across tranches (decimal, e.g. 0.08 = 8 %). */
112
139
  apy: number;
@@ -349,7 +349,12 @@ export async function checkBatchOutcome<R extends ReceiptLike = ReceiptLike>(
349
349
  return 'unknown';
350
350
  }
351
351
  if (read.kind === 'reverted') return 'not-deposited';
352
- if (read.kind !== 'mined' || typeof read.txHash !== 'string') {
352
+ // An empty hash is not an answer — `waitForMined` reads it the same way.
353
+ if (
354
+ read.kind !== 'mined' ||
355
+ typeof read.txHash !== 'string' ||
356
+ read.txHash === ''
357
+ ) {
353
358
  return 'unknown';
354
359
  }
355
360
  try {
@@ -367,9 +372,60 @@ export async function checkBatchOutcome<R extends ReceiptLike = ReceiptLike>(
367
372
  }
368
373
 
369
374
  // ---------------------------------------------------------------------------
370
- // One sponsored call — withdraw, cancel, withdraw at expiry
375
+ // One sponsored call — withdraw, cancel, withdraw at expiry, transfer
371
376
  // ---------------------------------------------------------------------------
372
377
 
378
+ /**
379
+ * `checkBatchOutcome` for ONE sponsored call — ONE status read, no polling —
380
+ * for an app's guard against sending the same request twice (a P2P transfer
381
+ * whose earlier submission ended PENDING, say):
382
+ *
383
+ * - `executed` — mined, and the receipt proves the sender's operation
384
+ * succeeded (`proveUserOperationInReceipt`);
385
+ * - `not-executed` — definitively reverted (vendor status or the operation's
386
+ * own flag);
387
+ * - `unknown` — anything else, including a read or receipt that failed, or a
388
+ * receipt that does not show the sender's operation.
389
+ *
390
+ * Only `not-executed` may let a new attempt proceed unprompted; `executed`
391
+ * means the earlier one landed and the user should be told.
392
+ */
393
+ export async function checkSponsoredCallOutcome<
394
+ R extends ReceiptLike = ReceiptLike,
395
+ >(
396
+ transactionId: string,
397
+ expect: Omit<UserOperationProofExpectation, 'userOperationHash'>,
398
+ ports: Pick<BatchSubmissionPorts<R>, 'getStatus' | 'getReceipt'>,
399
+ ): Promise<'executed' | 'not-executed' | 'unknown'> {
400
+ let read: BatchStatusRead;
401
+ try {
402
+ read = await ports.getStatus(transactionId);
403
+ } catch {
404
+ return 'unknown';
405
+ }
406
+ if (read.kind === 'reverted') return 'not-executed';
407
+ // An empty hash is not an answer — `waitForMined` reads it the same way.
408
+ if (
409
+ read.kind !== 'mined' ||
410
+ typeof read.txHash !== 'string' ||
411
+ read.txHash === ''
412
+ ) {
413
+ return 'unknown';
414
+ }
415
+ try {
416
+ const receipt = await ports.getReceipt(read.txHash);
417
+ const proof = proveUserOperationInReceipt(receipt, {
418
+ ...expect,
419
+ userOperationHash: read.userOperationHash ?? null,
420
+ });
421
+ if (proof.kind === 'executed') return 'executed';
422
+ if (proof.kind === 'reverted') return 'not-executed';
423
+ return 'unknown';
424
+ } catch {
425
+ return 'unknown';
426
+ }
427
+ }
428
+
373
429
  /**
374
430
  * `BatchSubmissionPorts` for ONE sponsored call: the same status, receipt and
375
431
  * clock ports — a relay client written for the deposit batch serves both —
@@ -409,9 +465,9 @@ export interface SponsoredCallSubmission<R extends ReceiptLike = ReceiptLike>
409
465
  }
410
466
 
411
467
  /**
412
- * `submitBatch` for ONE sponsored `LendingPoolManager` call — the body of a
413
- * `WithdrawFlow` `submitCall` port, and of an app's cancel or
414
- * withdraw-at-expiry action. Same post, same polling policy, same outcome
468
+ * `submitBatch` for ONE sponsored call — the body of a `WithdrawFlow`
469
+ * `submitCall` port, of an app's cancel or withdraw-at-expiry action, and of
470
+ * a P2P transfer (`encodeStableTransfer`). Same post, same polling policy, same outcome
415
471
  * table; the proof is `proveUserOperationInReceipt` (the sender's operation
416
472
  * succeeded) instead of the deposit proof, and an unproven receipt is
417
473
  * `call_unproven:<reason>`.
@@ -427,7 +483,8 @@ export interface SponsoredCallSubmission<R extends ReceiptLike = ReceiptLike>
427
483
  * The call is checked against its kind before anything is posted — a known
428
484
  * kind, exactly `{ to, data }`, and `data` opening with that kind's selector —
429
485
  * and a mismatch is `BatchSubmissionError('call_mismatch', 'failed')`: nothing
430
- * was sent. Build calls with `kasu.calls`, and this never fires.
486
+ * was sent. Build calls with `kasu.calls` (or `encodeStableTransfer`), and
487
+ * this never fires.
431
488
  *
432
489
  * As on the deposit path, nothing after the post ever produces `failed`.
433
490
  */
@@ -21,6 +21,7 @@ export type { BatchSubmissionOutcome } from './batch-errors';
21
21
 
22
22
  export {
23
23
  checkBatchOutcome,
24
+ checkSponsoredCallOutcome,
24
25
  DEFAULT_BATCH_POLLING_POLICY,
25
26
  submitBatch,
26
27
  submitSponsoredCall,