@kasufinance/kasu-sdk 2.10.1 → 2.10.3

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.
@@ -24219,9 +24219,10 @@ class SdkConfig {
24219
24219
  }
24220
24220
 
24221
24221
  /**
24222
- * The single-call `LendingPoolManager` requests a sponsored relay may carry,
24223
- * encoded by the SDK so no application hand-builds calldata or hand-maps the
24224
- * relay's `kind` for them.
24222
+ * The single-call requests a sponsored relay may carry — the four
24223
+ * `LendingPoolManager` requests, plus the stable asset's own `transfer` (P2P
24224
+ * Send) — encoded by the SDK so no application hand-builds calldata or
24225
+ * hand-maps the relay's `kind` for them.
24225
24226
  *
24226
24227
  * Every function here is PURE: addresses and ids in, `{ kind, call }` out.
24227
24228
  * Reading the chain (the shares a withdrawal is for) is the facade's job —
@@ -24249,6 +24250,11 @@ const SPONSORED_CALL_KINDS = [
24249
24250
  'cancel_withdrawal',
24250
24251
  /** `LendingPoolManager.requestFixedTermDepositWithdrawal(pool, lockId)`. */
24251
24252
  'withdraw_at_expiry',
24253
+ /**
24254
+ * `stableAsset.transfer(to, amount)` — P2P Send, to the chain's stable
24255
+ * asset rather than the manager. See {@link encodeStableTransfer}.
24256
+ */
24257
+ 'transfer',
24252
24258
  ];
24253
24259
  /**
24254
24260
  * Widened to the plain `Interface`: the typechain one overloads
@@ -24256,7 +24262,9 @@ const SPONSORED_CALL_KINDS = [
24256
24262
  * `FUNCTION_OF`.
24257
24263
  */
24258
24264
  const LENDING_POOL_MANAGER$2 = ILendingPoolManagerAbi__factory.createInterface();
24259
- /** The `LendingPoolManager` function behind each kind. */
24265
+ /** The generated ERC-20 interface — the `transfer` kind's one function. */
24266
+ const ERC20$2 = IERC20MetadataAbi__factory.createInterface();
24267
+ /** The `LendingPoolManager` function behind each manager kind. */
24260
24268
  const FUNCTION_OF = Object.freeze({
24261
24269
  withdrawal: 'requestWithdrawal',
24262
24270
  cancel_deposit: 'cancelDepositRequest',
@@ -24265,14 +24273,16 @@ const FUNCTION_OF = Object.freeze({
24265
24273
  });
24266
24274
  /**
24267
24275
  * The 4-byte selector of each kind's function, DERIVED from the generated
24268
- * `ILendingPoolManager` ABI — never typed in by hand, so an ABI change moves
24269
- * it and the cross-check spec against the relay's own pins goes red.
24276
+ * ABIs (`ILendingPoolManager`, and `IERC20Metadata` for `transfer`) — never
24277
+ * typed in by hand, so an ABI change moves it and the cross-check spec
24278
+ * against the relay's own pins goes red.
24270
24279
  */
24271
24280
  const SPONSORED_CALL_SELECTORS = Object.freeze({
24272
24281
  withdrawal: LENDING_POOL_MANAGER$2.getSighash(FUNCTION_OF.withdrawal),
24273
24282
  cancel_deposit: LENDING_POOL_MANAGER$2.getSighash(FUNCTION_OF.cancel_deposit),
24274
24283
  cancel_withdrawal: LENDING_POOL_MANAGER$2.getSighash(FUNCTION_OF.cancel_withdrawal),
24275
24284
  withdraw_at_expiry: LENDING_POOL_MANAGER$2.getSighash(FUNCTION_OF.withdraw_at_expiry),
24285
+ transfer: ERC20$2.getSighash('transfer'),
24276
24286
  });
24277
24287
  /**
24278
24288
  * What an encoder throws for an input it will not encode. `code` names the
@@ -24371,6 +24381,53 @@ function encodeWithdrawAtExpiryCall(lendingPoolManager, poolId, fixedTermDeposit
24371
24381
  toUint256(fixedTermDepositId, 'lock-id'),
24372
24382
  ]);
24373
24383
  }
24384
+ /**
24385
+ * `stableAsset.transfer(to, amount)` — `transfer`: P2P Send of the chain's
24386
+ * stable asset from the sender's own account (`msg.sender`) to `to`.
24387
+ *
24388
+ * `amount` is in the token's BASE units (6 decimals for USDC), and must be
24389
+ * greater than zero. `to` must be a well-formed, non-zero address (a
24390
+ * mixed-case one with a valid EIP-55 checksum). That is
24391
+ * ALL this encoder judges, because it is all a pure function can know.
24392
+ *
24393
+ * The recipient POLICY is the relay's job and is deliberately not restated
24394
+ * here: it refuses the sender's own account, the stable asset, the
24395
+ * `LendingPoolManager`, the precompile range, any address with contract code
24396
+ * (read on chain at submission), a sender our identity store does not hold
24397
+ * as verified, and anything over the sender's daily count and amount caps.
24398
+ * A call this encoder accepts can therefore still be refused — the app maps
24399
+ * the relay's refusal codes to its own words.
24400
+ *
24401
+ * Pass `stableAsset` from the chain config (`CHAIN_CONFIGS[chain].stableAsset
24402
+ * .address`) — the relay compares `to` against its own configured token and
24403
+ * refuses any other contract.
24404
+ */
24405
+ function encodeStableTransfer(input) {
24406
+ assertAddress(input.stableAsset, 'stable-asset');
24407
+ assertAddress(input.to, 'recipient');
24408
+ // A mixed-case recipient carries an EIP-55 checksum, and one that does not
24409
+ // match is a mistyped address: refuse it as `recipient` here rather than
24410
+ // let the ABI coder throw its own error at the encode below.
24411
+ try {
24412
+ ethers.utils.getAddress(input.to);
24413
+ }
24414
+ catch (_a) {
24415
+ throw new SponsoredCallInputError('recipient');
24416
+ }
24417
+ if (ethers.BigNumber.from(input.to).isZero()) {
24418
+ throw new SponsoredCallInputError('zero-recipient');
24419
+ }
24420
+ const amount = toUint256(input.amount, 'amount');
24421
+ if (amount.isZero())
24422
+ throw new SponsoredCallInputError('zero-amount');
24423
+ return {
24424
+ kind: 'transfer',
24425
+ call: {
24426
+ to: input.stableAsset,
24427
+ data: ERC20$2.encodeFunctionData('transfer', [input.to, amount]),
24428
+ },
24429
+ };
24430
+ }
24374
24431
 
24375
24432
  const ADDRESS_RE$1 = /^0x[0-9a-fA-F]{40}$/;
24376
24433
  /**
@@ -25102,26 +25159,33 @@ function sameAmount(a, b) {
25102
25159
  * this run's deposit.
25103
25160
  */
25104
25161
  function decodeCanonical(iface, name, data, code) {
25105
- let parsed;
25106
- try {
25107
- parsed = iface.parseTransaction({ data });
25108
- }
25109
- catch (_a) {
25110
- return mismatch(code);
25111
- }
25112
- if (parsed.name !== name)
25113
- return mismatch(code);
25162
+ // Decode AND re-encode against the interface's OWN fragment for `name`,
25163
+ // never through `parseTransaction`. ethers v5's `TransactionDescription`
25164
+ // deep-copies its fields, and a fragment whose `inputs` array is not
25165
+ // frozen comes out as a plain object. Under the CommonJS build (Node,
25166
+ // jest) that copy still carries `format()`, because ES5 prototype methods
25167
+ // are enumerable; under the ES-module build a browser bundler resolves
25168
+ // (`lib.esm`, real classes) it carries no methods at all, so re-encoding
25169
+ // `parsed.functionFragment` threw and EVERY batch was refused as
25170
+ // `approve-call` in the browser (2.10.0, 2.10.1). With no description in
25171
+ // the path, nothing here depends on which copied fields survive.
25172
+ //
25173
+ // `decodeFunctionData` refuses data whose selector is not this
25174
+ // fragment's, so a call to any other function is `code`, as before.
25175
+ let args;
25114
25176
  let canonical;
25115
25177
  try {
25116
- canonical = iface.encodeFunctionData(parsed.functionFragment, parsed.args);
25178
+ const fragment = iface.getFunction(name);
25179
+ args = iface.decodeFunctionData(fragment, data);
25180
+ canonical = iface.encodeFunctionData(fragment, args);
25117
25181
  }
25118
- catch (_b) {
25182
+ catch (_a) {
25119
25183
  return mismatch(code);
25120
25184
  }
25121
25185
  if (canonical.toLowerCase() !== data.toLowerCase()) {
25122
25186
  return mismatch('non-canonical-encoding');
25123
25187
  }
25124
- return parsed.args;
25188
+ return args;
25125
25189
  }
25126
25190
  /**
25127
25191
  * Pin a deposit batch to the run that produced it — PURE; throws a
@@ -25595,7 +25659,10 @@ function checkBatchOutcome(transactionId, expect, ports) {
25595
25659
  }
25596
25660
  if (read.kind === 'reverted')
25597
25661
  return 'not-deposited';
25598
- if (read.kind !== 'mined' || typeof read.txHash !== 'string') {
25662
+ // An empty hash is not an answer — `waitForMined` reads it the same way.
25663
+ if (read.kind !== 'mined' ||
25664
+ typeof read.txHash !== 'string' ||
25665
+ read.txHash === '') {
25599
25666
  return 'unknown';
25600
25667
  }
25601
25668
  try {
@@ -25612,10 +25679,60 @@ function checkBatchOutcome(transactionId, expect, ports) {
25612
25679
  }
25613
25680
  });
25614
25681
  }
25682
+ // ---------------------------------------------------------------------------
25683
+ // One sponsored call — withdraw, cancel, withdraw at expiry, transfer
25684
+ // ---------------------------------------------------------------------------
25685
+ /**
25686
+ * `checkBatchOutcome` for ONE sponsored call — ONE status read, no polling —
25687
+ * for an app's guard against sending the same request twice (a P2P transfer
25688
+ * whose earlier submission ended PENDING, say):
25689
+ *
25690
+ * - `executed` — mined, and the receipt proves the sender's operation
25691
+ * succeeded (`proveUserOperationInReceipt`);
25692
+ * - `not-executed` — definitively reverted (vendor status or the operation's
25693
+ * own flag);
25694
+ * - `unknown` — anything else, including a read or receipt that failed, or a
25695
+ * receipt that does not show the sender's operation.
25696
+ *
25697
+ * Only `not-executed` may let a new attempt proceed unprompted; `executed`
25698
+ * means the earlier one landed and the user should be told.
25699
+ */
25700
+ function checkSponsoredCallOutcome(transactionId, expect, ports) {
25701
+ return __awaiter(this, void 0, void 0, function* () {
25702
+ var _a;
25703
+ let read;
25704
+ try {
25705
+ read = yield ports.getStatus(transactionId);
25706
+ }
25707
+ catch (_b) {
25708
+ return 'unknown';
25709
+ }
25710
+ if (read.kind === 'reverted')
25711
+ return 'not-executed';
25712
+ // An empty hash is not an answer — `waitForMined` reads it the same way.
25713
+ if (read.kind !== 'mined' ||
25714
+ typeof read.txHash !== 'string' ||
25715
+ read.txHash === '') {
25716
+ return 'unknown';
25717
+ }
25718
+ try {
25719
+ const receipt = yield ports.getReceipt(read.txHash);
25720
+ const proof = proveUserOperationInReceipt(receipt, Object.assign(Object.assign({}, expect), { userOperationHash: (_a = read.userOperationHash) !== null && _a !== void 0 ? _a : null }));
25721
+ if (proof.kind === 'executed')
25722
+ return 'executed';
25723
+ if (proof.kind === 'reverted')
25724
+ return 'not-executed';
25725
+ return 'unknown';
25726
+ }
25727
+ catch (_c) {
25728
+ return 'unknown';
25729
+ }
25730
+ });
25731
+ }
25615
25732
  /**
25616
- * `submitBatch` for ONE sponsored `LendingPoolManager` call — the body of a
25617
- * `WithdrawFlow` `submitCall` port, and of an app's cancel or
25618
- * withdraw-at-expiry action. Same post, same polling policy, same outcome
25733
+ * `submitBatch` for ONE sponsored call — the body of a `WithdrawFlow`
25734
+ * `submitCall` port, of an app's cancel or withdraw-at-expiry action, and of
25735
+ * a P2P transfer (`encodeStableTransfer`). Same post, same polling policy, same outcome
25619
25736
  * table; the proof is `proveUserOperationInReceipt` (the sender's operation
25620
25737
  * succeeded) instead of the deposit proof, and an unproven receipt is
25621
25738
  * `call_unproven:<reason>`.
@@ -25631,7 +25748,8 @@ function checkBatchOutcome(transactionId, expect, ports) {
25631
25748
  * The call is checked against its kind before anything is posted — a known
25632
25749
  * kind, exactly `{ to, data }`, and `data` opening with that kind's selector —
25633
25750
  * and a mismatch is `BatchSubmissionError('call_mismatch', 'failed')`: nothing
25634
- * was sent. Build calls with `kasu.calls`, and this never fires.
25751
+ * was sent. Build calls with `kasu.calls` (or `encodeStableTransfer`), and
25752
+ * this never fires.
25635
25753
  *
25636
25754
  * As on the deposit path, nothing after the post ever produces `failed`.
25637
25755
  */
@@ -27610,7 +27728,9 @@ class StrategiesFacade {
27610
27728
  return {
27611
27729
  id: pool.id,
27612
27730
  name: pool.poolName,
27731
+ subheading: nonEmptyOrUndefined(pool.subheading),
27613
27732
  description: pool.description,
27733
+ liteDescription: nonEmptyOrUndefined(pool.liteDescription),
27614
27734
  isActive: pool.isActive,
27615
27735
  apy: pool.apy,
27616
27736
  tvl: pool.totalValueLocked,
@@ -27625,6 +27745,16 @@ class StrategiesFacade {
27625
27745
  };
27626
27746
  }
27627
27747
  }
27748
+ /**
27749
+ * A CMS text field as a string, or `undefined` when it is absent, not a
27750
+ * string, or empty (blank included) — the CMS stores an emptied field as `''`, and an empty
27751
+ * subheading must read as "none", not render an empty line.
27752
+ */
27753
+ function nonEmptyOrUndefined(value) {
27754
+ return typeof value === 'string' && value.trim().length > 0
27755
+ ? value
27756
+ : undefined;
27757
+ }
27628
27758
  /**
27629
27759
  * A CMS "numeric" field as a finite number, or `null`. An empty or blank
27630
27760
  * string is UNAUTHORED, not zero.
@@ -29025,6 +29155,7 @@ exports.buildLoanAgreementSignMessage = buildLoanAgreementSignMessage;
29025
29155
  exports.cancelDeadline = cancelDeadline;
29026
29156
  exports.ceilToCents = ceilToCents;
29027
29157
  exports.checkBatchOutcome = checkBatchOutcome;
29158
+ exports.checkSponsoredCallOutcome = checkSponsoredCallOutcome;
29028
29159
  exports.classifyWalletFailure = classifyWalletFailure;
29029
29160
  exports.compareTrancheSeniority = compareTrancheSeniority;
29030
29161
  exports.computeSettlementWindow = computeSettlementWindow;
@@ -29038,6 +29169,7 @@ exports.encodeCancelDepositCall = encodeCancelDepositCall;
29038
29169
  exports.encodeCancelWithdrawalCall = encodeCancelWithdrawalCall;
29039
29170
  exports.encodeDepositData = encodeDepositData;
29040
29171
  exports.encodeRequestWithdrawalCall = encodeRequestWithdrawalCall;
29172
+ exports.encodeStableTransfer = encodeStableTransfer;
29041
29173
  exports.encodeWithdrawAtExpiryCall = encodeWithdrawAtExpiryCall;
29042
29174
  exports.epochRateToApy = epochRateToApy;
29043
29175
  exports.explorerAddressUrl = explorerAddressUrl;
@@ -24217,9 +24217,10 @@ class SdkConfig {
24217
24217
  }
24218
24218
 
24219
24219
  /**
24220
- * The single-call `LendingPoolManager` requests a sponsored relay may carry,
24221
- * encoded by the SDK so no application hand-builds calldata or hand-maps the
24222
- * relay's `kind` for them.
24220
+ * The single-call requests a sponsored relay may carry — the four
24221
+ * `LendingPoolManager` requests, plus the stable asset's own `transfer` (P2P
24222
+ * Send) — encoded by the SDK so no application hand-builds calldata or
24223
+ * hand-maps the relay's `kind` for them.
24223
24224
  *
24224
24225
  * Every function here is PURE: addresses and ids in, `{ kind, call }` out.
24225
24226
  * Reading the chain (the shares a withdrawal is for) is the facade's job —
@@ -24247,6 +24248,11 @@ const SPONSORED_CALL_KINDS = [
24247
24248
  'cancel_withdrawal',
24248
24249
  /** `LendingPoolManager.requestFixedTermDepositWithdrawal(pool, lockId)`. */
24249
24250
  'withdraw_at_expiry',
24251
+ /**
24252
+ * `stableAsset.transfer(to, amount)` — P2P Send, to the chain's stable
24253
+ * asset rather than the manager. See {@link encodeStableTransfer}.
24254
+ */
24255
+ 'transfer',
24250
24256
  ];
24251
24257
  /**
24252
24258
  * Widened to the plain `Interface`: the typechain one overloads
@@ -24254,7 +24260,9 @@ const SPONSORED_CALL_KINDS = [
24254
24260
  * `FUNCTION_OF`.
24255
24261
  */
24256
24262
  const LENDING_POOL_MANAGER$2 = ILendingPoolManagerAbi__factory.createInterface();
24257
- /** The `LendingPoolManager` function behind each kind. */
24263
+ /** The generated ERC-20 interface — the `transfer` kind's one function. */
24264
+ const ERC20$2 = IERC20MetadataAbi__factory.createInterface();
24265
+ /** The `LendingPoolManager` function behind each manager kind. */
24258
24266
  const FUNCTION_OF = Object.freeze({
24259
24267
  withdrawal: 'requestWithdrawal',
24260
24268
  cancel_deposit: 'cancelDepositRequest',
@@ -24263,14 +24271,16 @@ const FUNCTION_OF = Object.freeze({
24263
24271
  });
24264
24272
  /**
24265
24273
  * The 4-byte selector of each kind's function, DERIVED from the generated
24266
- * `ILendingPoolManager` ABI — never typed in by hand, so an ABI change moves
24267
- * it and the cross-check spec against the relay's own pins goes red.
24274
+ * ABIs (`ILendingPoolManager`, and `IERC20Metadata` for `transfer`) — never
24275
+ * typed in by hand, so an ABI change moves it and the cross-check spec
24276
+ * against the relay's own pins goes red.
24268
24277
  */
24269
24278
  const SPONSORED_CALL_SELECTORS = Object.freeze({
24270
24279
  withdrawal: LENDING_POOL_MANAGER$2.getSighash(FUNCTION_OF.withdrawal),
24271
24280
  cancel_deposit: LENDING_POOL_MANAGER$2.getSighash(FUNCTION_OF.cancel_deposit),
24272
24281
  cancel_withdrawal: LENDING_POOL_MANAGER$2.getSighash(FUNCTION_OF.cancel_withdrawal),
24273
24282
  withdraw_at_expiry: LENDING_POOL_MANAGER$2.getSighash(FUNCTION_OF.withdraw_at_expiry),
24283
+ transfer: ERC20$2.getSighash('transfer'),
24274
24284
  });
24275
24285
  /**
24276
24286
  * What an encoder throws for an input it will not encode. `code` names the
@@ -24369,6 +24379,53 @@ function encodeWithdrawAtExpiryCall(lendingPoolManager, poolId, fixedTermDeposit
24369
24379
  toUint256(fixedTermDepositId, 'lock-id'),
24370
24380
  ]);
24371
24381
  }
24382
+ /**
24383
+ * `stableAsset.transfer(to, amount)` — `transfer`: P2P Send of the chain's
24384
+ * stable asset from the sender's own account (`msg.sender`) to `to`.
24385
+ *
24386
+ * `amount` is in the token's BASE units (6 decimals for USDC), and must be
24387
+ * greater than zero. `to` must be a well-formed, non-zero address (a
24388
+ * mixed-case one with a valid EIP-55 checksum). That is
24389
+ * ALL this encoder judges, because it is all a pure function can know.
24390
+ *
24391
+ * The recipient POLICY is the relay's job and is deliberately not restated
24392
+ * here: it refuses the sender's own account, the stable asset, the
24393
+ * `LendingPoolManager`, the precompile range, any address with contract code
24394
+ * (read on chain at submission), a sender our identity store does not hold
24395
+ * as verified, and anything over the sender's daily count and amount caps.
24396
+ * A call this encoder accepts can therefore still be refused — the app maps
24397
+ * the relay's refusal codes to its own words.
24398
+ *
24399
+ * Pass `stableAsset` from the chain config (`CHAIN_CONFIGS[chain].stableAsset
24400
+ * .address`) — the relay compares `to` against its own configured token and
24401
+ * refuses any other contract.
24402
+ */
24403
+ function encodeStableTransfer(input) {
24404
+ assertAddress(input.stableAsset, 'stable-asset');
24405
+ assertAddress(input.to, 'recipient');
24406
+ // A mixed-case recipient carries an EIP-55 checksum, and one that does not
24407
+ // match is a mistyped address: refuse it as `recipient` here rather than
24408
+ // let the ABI coder throw its own error at the encode below.
24409
+ try {
24410
+ utils$2.getAddress(input.to);
24411
+ }
24412
+ catch (_a) {
24413
+ throw new SponsoredCallInputError('recipient');
24414
+ }
24415
+ if (BigNumber.from(input.to).isZero()) {
24416
+ throw new SponsoredCallInputError('zero-recipient');
24417
+ }
24418
+ const amount = toUint256(input.amount, 'amount');
24419
+ if (amount.isZero())
24420
+ throw new SponsoredCallInputError('zero-amount');
24421
+ return {
24422
+ kind: 'transfer',
24423
+ call: {
24424
+ to: input.stableAsset,
24425
+ data: ERC20$2.encodeFunctionData('transfer', [input.to, amount]),
24426
+ },
24427
+ };
24428
+ }
24372
24429
 
24373
24430
  const ADDRESS_RE$1 = /^0x[0-9a-fA-F]{40}$/;
24374
24431
  /**
@@ -25100,26 +25157,33 @@ function sameAmount(a, b) {
25100
25157
  * this run's deposit.
25101
25158
  */
25102
25159
  function decodeCanonical(iface, name, data, code) {
25103
- let parsed;
25104
- try {
25105
- parsed = iface.parseTransaction({ data });
25106
- }
25107
- catch (_a) {
25108
- return mismatch(code);
25109
- }
25110
- if (parsed.name !== name)
25111
- return mismatch(code);
25160
+ // Decode AND re-encode against the interface's OWN fragment for `name`,
25161
+ // never through `parseTransaction`. ethers v5's `TransactionDescription`
25162
+ // deep-copies its fields, and a fragment whose `inputs` array is not
25163
+ // frozen comes out as a plain object. Under the CommonJS build (Node,
25164
+ // jest) that copy still carries `format()`, because ES5 prototype methods
25165
+ // are enumerable; under the ES-module build a browser bundler resolves
25166
+ // (`lib.esm`, real classes) it carries no methods at all, so re-encoding
25167
+ // `parsed.functionFragment` threw and EVERY batch was refused as
25168
+ // `approve-call` in the browser (2.10.0, 2.10.1). With no description in
25169
+ // the path, nothing here depends on which copied fields survive.
25170
+ //
25171
+ // `decodeFunctionData` refuses data whose selector is not this
25172
+ // fragment's, so a call to any other function is `code`, as before.
25173
+ let args;
25112
25174
  let canonical;
25113
25175
  try {
25114
- canonical = iface.encodeFunctionData(parsed.functionFragment, parsed.args);
25176
+ const fragment = iface.getFunction(name);
25177
+ args = iface.decodeFunctionData(fragment, data);
25178
+ canonical = iface.encodeFunctionData(fragment, args);
25115
25179
  }
25116
- catch (_b) {
25180
+ catch (_a) {
25117
25181
  return mismatch(code);
25118
25182
  }
25119
25183
  if (canonical.toLowerCase() !== data.toLowerCase()) {
25120
25184
  return mismatch('non-canonical-encoding');
25121
25185
  }
25122
- return parsed.args;
25186
+ return args;
25123
25187
  }
25124
25188
  /**
25125
25189
  * Pin a deposit batch to the run that produced it — PURE; throws a
@@ -25593,7 +25657,10 @@ function checkBatchOutcome(transactionId, expect, ports) {
25593
25657
  }
25594
25658
  if (read.kind === 'reverted')
25595
25659
  return 'not-deposited';
25596
- if (read.kind !== 'mined' || typeof read.txHash !== 'string') {
25660
+ // An empty hash is not an answer — `waitForMined` reads it the same way.
25661
+ if (read.kind !== 'mined' ||
25662
+ typeof read.txHash !== 'string' ||
25663
+ read.txHash === '') {
25597
25664
  return 'unknown';
25598
25665
  }
25599
25666
  try {
@@ -25610,10 +25677,60 @@ function checkBatchOutcome(transactionId, expect, ports) {
25610
25677
  }
25611
25678
  });
25612
25679
  }
25680
+ // ---------------------------------------------------------------------------
25681
+ // One sponsored call — withdraw, cancel, withdraw at expiry, transfer
25682
+ // ---------------------------------------------------------------------------
25683
+ /**
25684
+ * `checkBatchOutcome` for ONE sponsored call — ONE status read, no polling —
25685
+ * for an app's guard against sending the same request twice (a P2P transfer
25686
+ * whose earlier submission ended PENDING, say):
25687
+ *
25688
+ * - `executed` — mined, and the receipt proves the sender's operation
25689
+ * succeeded (`proveUserOperationInReceipt`);
25690
+ * - `not-executed` — definitively reverted (vendor status or the operation's
25691
+ * own flag);
25692
+ * - `unknown` — anything else, including a read or receipt that failed, or a
25693
+ * receipt that does not show the sender's operation.
25694
+ *
25695
+ * Only `not-executed` may let a new attempt proceed unprompted; `executed`
25696
+ * means the earlier one landed and the user should be told.
25697
+ */
25698
+ function checkSponsoredCallOutcome(transactionId, expect, ports) {
25699
+ return __awaiter(this, void 0, void 0, function* () {
25700
+ var _a;
25701
+ let read;
25702
+ try {
25703
+ read = yield ports.getStatus(transactionId);
25704
+ }
25705
+ catch (_b) {
25706
+ return 'unknown';
25707
+ }
25708
+ if (read.kind === 'reverted')
25709
+ return 'not-executed';
25710
+ // An empty hash is not an answer — `waitForMined` reads it the same way.
25711
+ if (read.kind !== 'mined' ||
25712
+ typeof read.txHash !== 'string' ||
25713
+ read.txHash === '') {
25714
+ return 'unknown';
25715
+ }
25716
+ try {
25717
+ const receipt = yield ports.getReceipt(read.txHash);
25718
+ const proof = proveUserOperationInReceipt(receipt, Object.assign(Object.assign({}, expect), { userOperationHash: (_a = read.userOperationHash) !== null && _a !== void 0 ? _a : null }));
25719
+ if (proof.kind === 'executed')
25720
+ return 'executed';
25721
+ if (proof.kind === 'reverted')
25722
+ return 'not-executed';
25723
+ return 'unknown';
25724
+ }
25725
+ catch (_c) {
25726
+ return 'unknown';
25727
+ }
25728
+ });
25729
+ }
25613
25730
  /**
25614
- * `submitBatch` for ONE sponsored `LendingPoolManager` call — the body of a
25615
- * `WithdrawFlow` `submitCall` port, and of an app's cancel or
25616
- * withdraw-at-expiry action. Same post, same polling policy, same outcome
25731
+ * `submitBatch` for ONE sponsored call — the body of a `WithdrawFlow`
25732
+ * `submitCall` port, of an app's cancel or withdraw-at-expiry action, and of
25733
+ * a P2P transfer (`encodeStableTransfer`). Same post, same polling policy, same outcome
25617
25734
  * table; the proof is `proveUserOperationInReceipt` (the sender's operation
25618
25735
  * succeeded) instead of the deposit proof, and an unproven receipt is
25619
25736
  * `call_unproven:<reason>`.
@@ -25629,7 +25746,8 @@ function checkBatchOutcome(transactionId, expect, ports) {
25629
25746
  * The call is checked against its kind before anything is posted — a known
25630
25747
  * kind, exactly `{ to, data }`, and `data` opening with that kind's selector —
25631
25748
  * and a mismatch is `BatchSubmissionError('call_mismatch', 'failed')`: nothing
25632
- * was sent. Build calls with `kasu.calls`, and this never fires.
25749
+ * was sent. Build calls with `kasu.calls` (or `encodeStableTransfer`), and
25750
+ * this never fires.
25633
25751
  *
25634
25752
  * As on the deposit path, nothing after the post ever produces `failed`.
25635
25753
  */
@@ -27608,7 +27726,9 @@ class StrategiesFacade {
27608
27726
  return {
27609
27727
  id: pool.id,
27610
27728
  name: pool.poolName,
27729
+ subheading: nonEmptyOrUndefined(pool.subheading),
27611
27730
  description: pool.description,
27731
+ liteDescription: nonEmptyOrUndefined(pool.liteDescription),
27612
27732
  isActive: pool.isActive,
27613
27733
  apy: pool.apy,
27614
27734
  tvl: pool.totalValueLocked,
@@ -27623,6 +27743,16 @@ class StrategiesFacade {
27623
27743
  };
27624
27744
  }
27625
27745
  }
27746
+ /**
27747
+ * A CMS text field as a string, or `undefined` when it is absent, not a
27748
+ * string, or empty (blank included) — the CMS stores an emptied field as `''`, and an empty
27749
+ * subheading must read as "none", not render an empty line.
27750
+ */
27751
+ function nonEmptyOrUndefined(value) {
27752
+ return typeof value === 'string' && value.trim().length > 0
27753
+ ? value
27754
+ : undefined;
27755
+ }
27626
27756
  /**
27627
27757
  * A CMS "numeric" field as a finite number, or `null`. An empty or blank
27628
27758
  * string is UNAUTHORED, not zero.
@@ -28963,4 +29093,4 @@ function weightedAverageApy(slices) {
28963
29093
  return weighted / totalBalance;
28964
29094
  }
28965
29095
 
28966
- export { APXIUM, AU_ALPHA3, AU_MIN_CUMULATIVE_BY_STABLE, BatchPendingError, BatchRevertedError, BatchSubmissionError, CHAIN_CONFIGS, CLEARING_WINDOW_SECONDS, CONTRACT_TTL_MS, CallsFacade, DAYS_PER_EPOCH, DAYS_PER_MONTH, DEFAULT_BATCH_POLLING_POLICY, DepositBatchMismatchError, DepositFlow, DepositsFacade, ENTRY_POINT_V07, EPOCHS_IN_YEAR, Flow, FlowStore, FlowsFacade, INVALID_ACCEPTANCE_SIGNATURE_MESSAGE, INVALID_AUTH_SIGNATURE_MESSAGE, INVOICEMATE, Kasu, KasuSdk, MAX_LENDING_AMOUNT_FALLBACK, MIN_DAYS_FOR_MONTHS, MIN_LENDING_AMOUNT_FALLBACK, MIN_TRANCHE_CAPACITY, NOT_A_WITHDRAWAL_CALL_MESSAGE, NO_DIRECTUS_URL_MESSAGE, NO_KYC_PARAMS_MESSAGE, NO_SPENDER_MESSAGE, NO_WITHDRAWAL_ENCODER_MESSAGE, PortfolioFacade, READ_ONLY_MESSAGE, RIXON_CAPITAL, SECONDS_PER_WEEK, SPONSORED_CALL_KINDS, SPONSORED_CALL_SELECTORS, SdkConfig, SponsoredCallInputError, StrategiesFacade, UPPER_MEZZANINE, WithdrawFlow, accruedInterest, apyToEpochRate, asContractType, auMinimumRemaining, auThresholdFor, availableFixedTermConfigs, batchOutcomeOf, buildContractVersionType, buildFullNameRequestMessage, buildLegacyContractRequestMessage, buildLoanAgreementSignMessage, cancelDeadline, ceilToCents, checkBatchOutcome, classifyWalletFailure, compareTrancheSeniority, computeSettlementWindow, countSubmissions, dailyInterest, decodeRevert, deriveCycleDates, derivePoolStatus, deriveRequestState, encodeCancelDepositCall, encodeCancelWithdrawalCall, encodeDepositData, encodeRequestWithdrawalCall, encodeWithdrawAtExpiryCall, epochRateToApy, explorerAddressUrl, explorerTxUrl, extractRevertData, fetchUnusedPoolIds, firstSubmissionTimestamp, floorToCents, formatSignTimestampUtc, getCreditOriginator, getInstitutionalLender, getStrategyRegion, getTrancheDisplayName, isAuMinimumExempt, isAustralianKyc, isBelowMinimumCapacity, isCycleClosed, isDefinitivelyNotSubmitted, isFixedTermConfigId, isUnpredictableGas, isUserRejected, lastEventTimestamp, lockDurationDays, lockDurationMonths, maxNetRateCeiling, netEffectiveApy, netTrancheApyBounds, nextCycleBoundary, parseFormattedMessage, parseMinorUnits, parseTrancheBound, pickDefaultTrancheId, pickHighestYieldTranche, poolAllTranchesFull, poolMaxApy, projectedAnnualEarnings, proveDepositInReceipt, proveUserOperationInReceipt, resolveBoundShortcuts, resolveDepositBounds, selectVisiblePools, submissionEvents, submitBatch, submitSponsoredCall, trancheApyBounds, trancheHasCapacity, trancheRiskRank, validateDepositBatchCalls, weeklyInterest, weightedAverageApy };
29096
+ export { APXIUM, AU_ALPHA3, AU_MIN_CUMULATIVE_BY_STABLE, BatchPendingError, BatchRevertedError, BatchSubmissionError, CHAIN_CONFIGS, CLEARING_WINDOW_SECONDS, CONTRACT_TTL_MS, CallsFacade, DAYS_PER_EPOCH, DAYS_PER_MONTH, DEFAULT_BATCH_POLLING_POLICY, DepositBatchMismatchError, DepositFlow, DepositsFacade, ENTRY_POINT_V07, EPOCHS_IN_YEAR, Flow, FlowStore, FlowsFacade, INVALID_ACCEPTANCE_SIGNATURE_MESSAGE, INVALID_AUTH_SIGNATURE_MESSAGE, INVOICEMATE, Kasu, KasuSdk, MAX_LENDING_AMOUNT_FALLBACK, MIN_DAYS_FOR_MONTHS, MIN_LENDING_AMOUNT_FALLBACK, MIN_TRANCHE_CAPACITY, NOT_A_WITHDRAWAL_CALL_MESSAGE, NO_DIRECTUS_URL_MESSAGE, NO_KYC_PARAMS_MESSAGE, NO_SPENDER_MESSAGE, NO_WITHDRAWAL_ENCODER_MESSAGE, PortfolioFacade, READ_ONLY_MESSAGE, RIXON_CAPITAL, SECONDS_PER_WEEK, SPONSORED_CALL_KINDS, SPONSORED_CALL_SELECTORS, SdkConfig, SponsoredCallInputError, StrategiesFacade, UPPER_MEZZANINE, WithdrawFlow, accruedInterest, apyToEpochRate, asContractType, auMinimumRemaining, auThresholdFor, availableFixedTermConfigs, batchOutcomeOf, buildContractVersionType, buildFullNameRequestMessage, buildLegacyContractRequestMessage, buildLoanAgreementSignMessage, cancelDeadline, ceilToCents, checkBatchOutcome, checkSponsoredCallOutcome, classifyWalletFailure, compareTrancheSeniority, computeSettlementWindow, countSubmissions, dailyInterest, decodeRevert, deriveCycleDates, derivePoolStatus, deriveRequestState, encodeCancelDepositCall, encodeCancelWithdrawalCall, encodeDepositData, encodeRequestWithdrawalCall, encodeStableTransfer, encodeWithdrawAtExpiryCall, epochRateToApy, explorerAddressUrl, explorerTxUrl, extractRevertData, fetchUnusedPoolIds, firstSubmissionTimestamp, floorToCents, formatSignTimestampUtc, getCreditOriginator, getInstitutionalLender, getStrategyRegion, getTrancheDisplayName, isAuMinimumExempt, isAustralianKyc, isBelowMinimumCapacity, isCycleClosed, isDefinitivelyNotSubmitted, isFixedTermConfigId, isUnpredictableGas, isUserRejected, lastEventTimestamp, lockDurationDays, lockDurationMonths, maxNetRateCeiling, netEffectiveApy, netTrancheApyBounds, nextCycleBoundary, parseFormattedMessage, parseMinorUnits, parseTrancheBound, pickDefaultTrancheId, pickHighestYieldTranche, poolAllTranchesFull, poolMaxApy, projectedAnnualEarnings, proveDepositInReceipt, proveUserOperationInReceipt, resolveBoundShortcuts, resolveDepositBounds, selectVisiblePools, submissionEvents, submitBatch, submitSponsoredCall, trancheApyBounds, trancheHasCapacity, trancheRiskRank, validateDepositBatchCalls, weeklyInterest, weightedAverageApy };
@@ -98,26 +98,33 @@ function sameAmount(a, b) {
98
98
  * this run's deposit.
99
99
  */
100
100
  function decodeCanonical(iface, name, data, code) {
101
- let parsed;
102
- try {
103
- parsed = iface.parseTransaction({ data });
104
- }
105
- catch (_a) {
106
- return mismatch(code);
107
- }
108
- if (parsed.name !== name)
109
- return mismatch(code);
101
+ // Decode AND re-encode against the interface's OWN fragment for `name`,
102
+ // never through `parseTransaction`. ethers v5's `TransactionDescription`
103
+ // deep-copies its fields, and a fragment whose `inputs` array is not
104
+ // frozen comes out as a plain object. Under the CommonJS build (Node,
105
+ // jest) that copy still carries `format()`, because ES5 prototype methods
106
+ // are enumerable; under the ES-module build a browser bundler resolves
107
+ // (`lib.esm`, real classes) it carries no methods at all, so re-encoding
108
+ // `parsed.functionFragment` threw and EVERY batch was refused as
109
+ // `approve-call` in the browser (2.10.0, 2.10.1). With no description in
110
+ // the path, nothing here depends on which copied fields survive.
111
+ //
112
+ // `decodeFunctionData` refuses data whose selector is not this
113
+ // fragment's, so a call to any other function is `code`, as before.
114
+ let args;
110
115
  let canonical;
111
116
  try {
112
- canonical = iface.encodeFunctionData(parsed.functionFragment, parsed.args);
117
+ const fragment = iface.getFunction(name);
118
+ args = iface.decodeFunctionData(fragment, data);
119
+ canonical = iface.encodeFunctionData(fragment, args);
113
120
  }
114
- catch (_b) {
121
+ catch (_a) {
115
122
  return mismatch(code);
116
123
  }
117
124
  if (canonical.toLowerCase() !== data.toLowerCase()) {
118
125
  return mismatch('non-canonical-encoding');
119
126
  }
120
- return parsed.args;
127
+ return args;
121
128
  }
122
129
  /**
123
130
  * Pin a deposit batch to the run that produced it — PURE; throws a