@lifi/composer-sdk 0.3.0 → 0.4.0-staging.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 (54) hide show
  1. package/README.md +21 -0
  2. package/dist/client.cjs +1 -1
  3. package/dist/client.cjs.map +1 -1
  4. package/dist/client.js +1 -1
  5. package/dist/client.js.map +1 -1
  6. package/dist/errors.cjs +26 -3
  7. package/dist/errors.cjs.map +1 -1
  8. package/dist/errors.d.cts +46 -2
  9. package/dist/errors.d.ts +46 -2
  10. package/dist/errors.js +24 -2
  11. package/dist/errors.js.map +1 -1
  12. package/dist/generated/materialisers.generated.cjs +8 -2
  13. package/dist/generated/materialisers.generated.cjs.map +1 -1
  14. package/dist/generated/materialisers.generated.d.cts +1 -1
  15. package/dist/generated/materialisers.generated.d.ts +1 -1
  16. package/dist/generated/materialisers.generated.js +6 -1
  17. package/dist/generated/materialisers.generated.js.map +1 -1
  18. package/dist/generated/operations.generated.cjs +39 -1
  19. package/dist/generated/operations.generated.cjs.map +1 -1
  20. package/dist/generated/operations.generated.d.cts +111 -2
  21. package/dist/generated/operations.generated.d.ts +111 -2
  22. package/dist/generated/operations.generated.js +39 -1
  23. package/dist/generated/operations.generated.js.map +1 -1
  24. package/dist/index.cjs +2 -0
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +3 -3
  27. package/dist/index.d.ts +3 -3
  28. package/dist/index.js +2 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/{materialisers.generated-BMkBQgWZ.d.ts → materialisers.generated-BfWJ-g7t.d.ts} +8 -3
  31. package/dist/{materialisers.generated-BWq5oePO.d.cts → materialisers.generated-DaJRwiRe.d.cts} +8 -3
  32. package/dist/responseSchemas.cjs +10 -1
  33. package/dist/responseSchemas.cjs.map +1 -1
  34. package/dist/responseSchemas.d.cts +15 -1
  35. package/dist/responseSchemas.d.ts +15 -1
  36. package/dist/responseSchemas.js +10 -1
  37. package/dist/responseSchemas.js.map +1 -1
  38. package/package.json +3 -4
  39. package/src/errors.ts +67 -1
  40. package/src/examples/staged/aaveBorrow.ts +128 -0
  41. package/src/examples/staged/aaveDebtRebalanceExactOut.ts +301 -0
  42. package/src/examples/staged/aaveHealthFactorExplode.ts +70 -0
  43. package/src/examples/staged/aaveToMorphoDebtMigration.ts +200 -0
  44. package/src/examples/staged/aaveToMorphoDebtMigrationWithSwap.ts +231 -0
  45. package/src/examples/staged/aaveToMorphoDebtMigrationWithSwapExactOut.ts +307 -0
  46. package/src/examples/staged/flashloanRepay.ts +142 -0
  47. package/src/examples/staged/manualRelayContinuation.ts +161 -0
  48. package/src/examples/staged/morphoBlueBorrow.ts +93 -0
  49. package/src/examples/staged/morphoBlueRepay.ts +132 -0
  50. package/src/examples/staged/registry.ts +89 -0
  51. package/src/generated/materialisers.generated.ts +12 -0
  52. package/src/generated/operations.generated.ts +214 -0
  53. package/src/index.ts +7 -2
  54. package/src/responseSchemas.ts +29 -0
@@ -20,9 +20,11 @@ import type { TypedGuard } from '../types.js';
20
20
  import type {
21
21
  AaveBorrowConfig,
22
22
  AaveClaimRewardsConfig,
23
+ AaveGetHealthFactorConfig,
23
24
  AaveRepayConfig,
24
25
  AaveRepayWithATokensConfig,
25
26
  AaveSetEModeConfig,
27
+ ContinuationAssertReadyConfig,
26
28
  CoreApproveConfig,
27
29
  CoreAsResourceConfig,
28
30
  CoreBalanceOfConfig,
@@ -46,10 +48,16 @@ import type {
46
48
  InvariantNeqConfig,
47
49
  InvariantNumericConfig,
48
50
  InvariantNumericInRangeConfig,
51
+ LifiFlashloanRepayConfig,
49
52
  LifiSwapConfig,
50
53
  LifiZapConfig,
54
+ MorphoBlueBorrowConfig,
55
+ MorphoBlueGetPositionConfig,
56
+ MorphoBlueRepayConfig,
51
57
  MorphoBlueSupplyCollateralConfig,
58
+ MorphoBlueSupplyConfig,
52
59
  MorphoBlueWithdrawCollateralConfig,
60
+ MorphoBlueWithdrawConfig,
53
61
  ParaswapBuyConfig,
54
62
  } from './config.generated.js';
55
63
 
@@ -500,6 +508,13 @@ export interface AaveSetEModeArgs {
500
508
  readonly guards?: readonly TypedGuard<never>[];
501
509
  }
502
510
 
511
+ /** Read the current Aave v3 health factor (WAD-scaled, 1e18; type(uint256).max for users with no debt) by static-calling IPool.getUserAccountData and decomposing the returned tuple. Exposes the healthFactor element as a uint256 handle suitable for use with core.numericInvariant. */
512
+ export interface AaveGetHealthFactorArgs {
513
+ readonly bind: Record<string, never>;
514
+ readonly config: AaveGetHealthFactorConfig;
515
+ readonly guards?: readonly TypedGuard<'healthFactor'>[];
516
+ }
517
+
503
518
  /** Deposit the collateral asset of a Morpho Blue market on behalf of the execution proxy. mode: 'exact' pulls the input handle's amount. mode: 'all' reads the proxy's current ERC20 balance of the market's collateral token and pulls that amount, ignoring the input handle's literal value — note this pulls the proxy's total balance (including unrelated upstream residuals), not just the input port's delivery. Credits Position.collateral on the Morpho singleton; emits no return value and exposes no resource output (the collateral is non-transferable accounting state). */
504
519
  export interface MorphoBlueSupplyCollateralArgs {
505
520
  readonly bind: {
@@ -516,6 +531,77 @@ export interface MorphoBlueWithdrawCollateralArgs {
516
531
  readonly guards?: readonly TypedGuard<'collateralWithdrawn'>[];
517
532
  }
518
533
 
534
+ /** Borrow the loan asset from a Morpho Blue market against existing collateral. Exposes the borrowed amount as a Resource and the (assetsBorrowed, sharesBorrowed) tuple as handles. */
535
+ export interface MorphoBlueBorrowArgs {
536
+ readonly bind: Record<string, never>;
537
+ readonly config: MorphoBlueBorrowConfig;
538
+ readonly guards?: readonly TypedGuard<'assetsBorrowed' | 'sharesBorrowed'>[];
539
+ }
540
+
541
+ /** Repay a Morpho Blue debt position with the supplied asset. mode: 'exact' repays the input handle amount; mode: 'max' reads Position.borrowShares via a precursor static call and repays in shares mode to retire the debt to the wei. Returns any unspent residual as a Resource. */
542
+ export interface MorphoBlueRepayArgs {
543
+ readonly bind: {
544
+ readonly assetIn: Bindable<'resource'>;
545
+ readonly onBehalfOf: Bindable<'address'>;
546
+ };
547
+ readonly config: MorphoBlueRepayConfig;
548
+ readonly guards?: readonly TypedGuard<
549
+ 'actualRepaid' | 'sharesRepaid' | 'residual'
550
+ >[];
551
+ }
552
+
553
+ /** Supply the loan asset of a Morpho Blue market and receive supplyShares accounting credit. Returns the (assetsSupplied, sharesSupplied) tuple as handles and any unspent residual as a Resource. */
554
+ export interface MorphoBlueSupplyArgs {
555
+ readonly bind: {
556
+ readonly assetIn: Bindable<'resource'>;
557
+ };
558
+ readonly config: MorphoBlueSupplyConfig;
559
+ readonly guards?: readonly TypedGuard<
560
+ 'assetsSupplied' | 'sharesSupplied' | 'residual'
561
+ >[];
562
+ }
563
+
564
+ /** Withdraw the loan asset from a Morpho Blue supply position. mode: 'exact' withdraws the configured amount; mode: 'max' reads Position.supplyShares via a precursor static call and burns all shares to retire the supply to the wei. Exposes the withdrawn amount as a Resource. */
565
+ export interface MorphoBlueWithdrawArgs {
566
+ readonly bind: Record<string, never>;
567
+ readonly config: MorphoBlueWithdrawConfig;
568
+ readonly guards?: readonly TypedGuard<
569
+ 'assetsWithdrawn' | 'sharesWithdrawn'
570
+ >[];
571
+ }
572
+
573
+ /** Read a Morpho Blue Position struct via a static call on the singleton. Exposes (supplyShares, borrowShares, collateral) as handles whose kinds match the on-chain field types (uint256, uint128, uint128). */
574
+ export interface MorphoBlueGetPositionArgs {
575
+ readonly bind: Record<string, never>;
576
+ readonly config: MorphoBlueGetPositionConfig;
577
+ readonly guards?: readonly TypedGuard<
578
+ 'supplyShares' | 'borrowShares' | 'collateral'
579
+ >[];
580
+ }
581
+
582
+ /** Transfer principal + fee back to the LiFi flashloan adapter for one leg */
583
+ export interface LifiFlashloanRepayArgs {
584
+ readonly bind: {
585
+ readonly funds: Bindable<'resource'>;
586
+ readonly feeBuffer?: Bindable<'resource'>;
587
+ };
588
+ readonly config: LifiFlashloanRepayConfig;
589
+ readonly guards?: readonly TypedGuard<never>[];
590
+ }
591
+
592
+ /** Assert a continuation readiness check on-chain; reverts the second leg if its proceeds have not landed */
593
+ export interface ContinuationAssertReadyArgs {
594
+ readonly bind: Record<string, never>;
595
+ readonly config: ContinuationAssertReadyConfig;
596
+ readonly guards?: readonly TypedGuard<never>[];
597
+ }
598
+
599
+ /** Carries a committed continuation thunk as node metadata for the settlement echo; emits no IR1 */
600
+ export interface ContinuationCommitmentArgs {
601
+ readonly bind: Record<string, never>;
602
+ readonly guards?: readonly TypedGuard<never>[];
603
+ }
604
+
519
605
  /** Route tokens into a DeFi protocol position via the edge catalog */
520
606
  export interface LifiZapArgs {
521
607
  readonly bind: {
@@ -620,11 +706,45 @@ export interface AaveClaimRewardsOutputs {
620
706
  readonly claimedAmount: OutputHandle<'uint256'>;
621
707
  }
622
708
 
709
+ export interface AaveGetHealthFactorOutputs {
710
+ readonly healthFactor: OutputHandle<'uint256'>;
711
+ }
712
+
623
713
  export interface MorphoBlueWithdrawCollateralOutputs {
624
714
  readonly withdrawn: OutputHandle<'resource'>;
625
715
  readonly collateralWithdrawn: OutputHandle<'uint256'>;
626
716
  }
627
717
 
718
+ export interface MorphoBlueBorrowOutputs {
719
+ readonly borrowed: OutputHandle<'resource'>;
720
+ readonly assetsBorrowed: OutputHandle<'uint256'>;
721
+ readonly sharesBorrowed: OutputHandle<'uint256'>;
722
+ }
723
+
724
+ export interface MorphoBlueRepayOutputs {
725
+ readonly actualRepaid: OutputHandle<'uint256'>;
726
+ readonly sharesRepaid: OutputHandle<'uint256'>;
727
+ readonly residual: OutputHandle<'resource'>;
728
+ }
729
+
730
+ export interface MorphoBlueSupplyOutputs {
731
+ readonly assetsSupplied: OutputHandle<'uint256'>;
732
+ readonly sharesSupplied: OutputHandle<'uint256'>;
733
+ readonly residual: OutputHandle<'resource'>;
734
+ }
735
+
736
+ export interface MorphoBlueWithdrawOutputs {
737
+ readonly withdrawn: OutputHandle<'resource'>;
738
+ readonly assetsWithdrawn: OutputHandle<'uint256'>;
739
+ readonly sharesWithdrawn: OutputHandle<'uint256'>;
740
+ }
741
+
742
+ export interface MorphoBlueGetPositionOutputs {
743
+ readonly supplyShares: OutputHandle<'uint256'>;
744
+ readonly borrowShares: OutputHandle<'uint128'>;
745
+ readonly collateral: OutputHandle<'uint128'>;
746
+ }
747
+
628
748
  export interface LifiZapOutputs {
629
749
  readonly amountOut: OutputHandle<'resource'>;
630
750
  }
@@ -739,6 +859,11 @@ export type GeneratedOps = {
739
859
  readonly lifi: {
740
860
  /** Swap tokens via the LI.FI aggregator API — original id: "lifi.swap" */
741
861
  readonly swap: (nodeId: string, args: LifiSwapArgs) => LifiSwapOutputs;
862
+ /** Transfer principal + fee back to the LiFi flashloan adapter for one leg — original id: "lifi.flashloanRepay" */
863
+ readonly flashloanRepay: (
864
+ nodeId: string,
865
+ args: LifiFlashloanRepayArgs,
866
+ ) => void;
742
867
  /** Route tokens into a DeFi protocol position via the edge catalog — original id: "lifi.zap" */
743
868
  readonly zap: (nodeId: string, args: LifiZapArgs) => LifiZapOutputs;
744
869
  };
@@ -823,6 +948,11 @@ export type GeneratedOps = {
823
948
  ) => AaveClaimRewardsOutputs;
824
949
  /** Switch the executionAddress's Aave v3 eMode category. 0 disables eMode. The pool reverts if the switch would make the position unhealthy or if any open borrow is for an asset outside the target category. — original id: "aave.setEMode" */
825
950
  readonly setEMode: (nodeId: string, args: AaveSetEModeArgs) => void;
951
+ /** Read the current Aave v3 health factor (WAD-scaled, 1e18; type(uint256).max for users with no debt) by static-calling IPool.getUserAccountData and decomposing the returned tuple. Exposes the healthFactor element as a uint256 handle suitable for use with core.numericInvariant. — original id: "aave.getHealthFactor" */
952
+ readonly getHealthFactor: (
953
+ nodeId: string,
954
+ args: AaveGetHealthFactorArgs,
955
+ ) => AaveGetHealthFactorOutputs;
826
956
  };
827
957
  readonly morphoBlue: {
828
958
  /** Deposit the collateral asset of a Morpho Blue market on behalf of the execution proxy. mode: 'exact' pulls the input handle's amount. mode: 'all' reads the proxy's current ERC20 balance of the market's collateral token and pulls that amount, ignoring the input handle's literal value — note this pulls the proxy's total balance (including unrelated upstream residuals), not just the input port's delivery. Credits Position.collateral on the Morpho singleton; emits no return value and exposes no resource output (the collateral is non-transferable accounting state). — original id: "morphoBlue.supplyCollateral" */
@@ -835,6 +965,43 @@ export type GeneratedOps = {
835
965
  nodeId: string,
836
966
  args: MorphoBlueWithdrawCollateralArgs,
837
967
  ) => MorphoBlueWithdrawCollateralOutputs;
968
+ /** Borrow the loan asset from a Morpho Blue market against existing collateral. Exposes the borrowed amount as a Resource and the (assetsBorrowed, sharesBorrowed) tuple as handles. — original id: "morphoBlue.borrow" */
969
+ readonly borrow: (
970
+ nodeId: string,
971
+ args: MorphoBlueBorrowArgs,
972
+ ) => MorphoBlueBorrowOutputs;
973
+ /** Repay a Morpho Blue debt position with the supplied asset. mode: 'exact' repays the input handle amount; mode: 'max' reads Position.borrowShares via a precursor static call and repays in shares mode to retire the debt to the wei. Returns any unspent residual as a Resource. — original id: "morphoBlue.repay" */
974
+ readonly repay: (
975
+ nodeId: string,
976
+ args: MorphoBlueRepayArgs,
977
+ ) => MorphoBlueRepayOutputs;
978
+ /** Supply the loan asset of a Morpho Blue market and receive supplyShares accounting credit. Returns the (assetsSupplied, sharesSupplied) tuple as handles and any unspent residual as a Resource. — original id: "morphoBlue.supply" */
979
+ readonly supply: (
980
+ nodeId: string,
981
+ args: MorphoBlueSupplyArgs,
982
+ ) => MorphoBlueSupplyOutputs;
983
+ /** Withdraw the loan asset from a Morpho Blue supply position. mode: 'exact' withdraws the configured amount; mode: 'max' reads Position.supplyShares via a precursor static call and burns all shares to retire the supply to the wei. Exposes the withdrawn amount as a Resource. — original id: "morphoBlue.withdraw" */
984
+ readonly withdraw: (
985
+ nodeId: string,
986
+ args: MorphoBlueWithdrawArgs,
987
+ ) => MorphoBlueWithdrawOutputs;
988
+ /** Read a Morpho Blue Position struct via a static call on the singleton. Exposes (supplyShares, borrowShares, collateral) as handles whose kinds match the on-chain field types (uint256, uint128, uint128). — original id: "morphoBlue.getPosition" */
989
+ readonly getPosition: (
990
+ nodeId: string,
991
+ args: MorphoBlueGetPositionArgs,
992
+ ) => MorphoBlueGetPositionOutputs;
993
+ };
994
+ readonly continuation: {
995
+ /** Assert a continuation readiness check on-chain; reverts the second leg if its proceeds have not landed — original id: "continuation.assertReady" */
996
+ readonly assertReady: (
997
+ nodeId: string,
998
+ args: ContinuationAssertReadyArgs,
999
+ ) => void;
1000
+ /** Carries a committed continuation thunk as node metadata for the settlement echo; emits no IR1 — original id: "continuation.commitment" */
1001
+ readonly commitment: (
1002
+ nodeId: string,
1003
+ args: ContinuationCommitmentArgs,
1004
+ ) => void;
838
1005
  };
839
1006
  readonly paraswap: {
840
1007
  /** Exact-output (BUY) swap via the Paraswap/Velora Market API: produce exactly `exactAmountOut` of the destination token, pulling only the source needed and exposing the genuinely-unspent source as `unspentIn`. — original id: "paraswap.buy" */
@@ -956,6 +1123,8 @@ export const bindGeneratedOps = (
956
1123
  amountOut: 'resource',
957
1124
  unspentIn: 'resource',
958
1125
  } as const),
1126
+ flashloanRepay: (nodeId: string, args: LifiFlashloanRepayArgs) =>
1127
+ builder.call(nodeId, 'lifi.flashloanRepay', args, {}),
959
1128
  zap: (nodeId: string, args: LifiZapArgs) =>
960
1129
  builder.call(nodeId, 'lifi.zap', args, {
961
1130
  amountOut: 'resource',
@@ -1058,6 +1227,10 @@ export const bindGeneratedOps = (
1058
1227
  } as const),
1059
1228
  setEMode: (nodeId: string, args: AaveSetEModeArgs) =>
1060
1229
  builder.call(nodeId, 'aave.setEMode', args, {}),
1230
+ getHealthFactor: (nodeId: string, args: AaveGetHealthFactorArgs) =>
1231
+ builder.call(nodeId, 'aave.getHealthFactor', args, {
1232
+ healthFactor: 'uint256',
1233
+ } as const),
1061
1234
  },
1062
1235
  morphoBlue: {
1063
1236
  supplyCollateral: (nodeId: string, args: MorphoBlueSupplyCollateralArgs) =>
@@ -1070,6 +1243,47 @@ export const bindGeneratedOps = (
1070
1243
  withdrawn: 'resource',
1071
1244
  collateralWithdrawn: 'uint256',
1072
1245
  } as const),
1246
+ borrow: (nodeId: string, args: MorphoBlueBorrowArgs) =>
1247
+ builder.call(nodeId, 'morphoBlue.borrow', args, {
1248
+ borrowed: 'resource',
1249
+ assetsBorrowed: 'uint256',
1250
+ sharesBorrowed: 'uint256',
1251
+ } as const),
1252
+ repay: (nodeId: string, args: MorphoBlueRepayArgs) =>
1253
+ builder.call(nodeId, 'morphoBlue.repay', args, {
1254
+ actualRepaid: 'uint256',
1255
+ sharesRepaid: 'uint256',
1256
+ residual: 'resource',
1257
+ } as const),
1258
+ supply: (nodeId: string, args: MorphoBlueSupplyArgs) =>
1259
+ builder.call(nodeId, 'morphoBlue.supply', args, {
1260
+ assetsSupplied: 'uint256',
1261
+ sharesSupplied: 'uint256',
1262
+ residual: 'resource',
1263
+ } as const),
1264
+ withdraw: (nodeId: string, args: MorphoBlueWithdrawArgs) =>
1265
+ builder.call(nodeId, 'morphoBlue.withdraw', args, {
1266
+ withdrawn: 'resource',
1267
+ assetsWithdrawn: 'uint256',
1268
+ sharesWithdrawn: 'uint256',
1269
+ } as const),
1270
+ getPosition: (nodeId: string, args: MorphoBlueGetPositionArgs) =>
1271
+ builder.call(nodeId, 'morphoBlue.getPosition', args, {
1272
+ supplyShares: 'uint256',
1273
+ borrowShares: 'uint128',
1274
+ collateral: 'uint128',
1275
+ } as const),
1276
+ },
1277
+ continuation: {
1278
+ assertReady: (nodeId: string, args: ContinuationAssertReadyArgs) =>
1279
+ builder.call(nodeId, 'continuation.assertReady', args, {}),
1280
+ commitment: (nodeId: string, args: ContinuationCommitmentArgs) =>
1281
+ builder.call(
1282
+ nodeId,
1283
+ 'continuation.commitment',
1284
+ { ...args, config: {} },
1285
+ {},
1286
+ ),
1073
1287
  },
1074
1288
  paraswap: {
1075
1289
  buy: (nodeId: string, args: ParaswapBuyArgs) =>
package/src/index.ts CHANGED
@@ -26,7 +26,11 @@ export type {
26
26
  ZapPackEdge,
27
27
  ZapPackOverview,
28
28
  } from './discovery.js';
29
- export type { ComposeError, ComposeErrorCode } from './errors.js';
29
+ export type {
30
+ ComposeError,
31
+ ComposeErrorCode,
32
+ ComposePreparationOps,
33
+ } from './errors.js';
30
34
  export type {
31
35
  AppliedGuard,
32
36
  ApprovalEntry,
@@ -38,6 +42,7 @@ export type {
38
42
  ComposeContinuation,
39
43
  ComposeErrorKind,
40
44
  ComposeTransactionRequest,
45
+ FailedPreparedOp,
41
46
  Flow,
42
47
  InputSpec,
43
48
  MaterialiserConfigOf,
@@ -74,7 +79,7 @@ export {
74
79
  SIMULATE_MAX_TRACKED_BALANCES,
75
80
  SIMULATE_MAX_REQUIREMENTS,
76
81
  } from '@lifi/compose-spec';
77
- export { isComposeError } from './errors.js';
82
+ export { isComposeError, isComposePreparationError } from './errors.js';
78
83
 
79
84
  export type {
80
85
  Bindable,
@@ -112,6 +112,19 @@ export const parseCompilePartialEnvelope = (
112
112
  return parsed.success ? parsed.data : null;
113
113
  };
114
114
 
115
+ /**
116
+ * A single prepared-op failure inside a `preparation_error` envelope. `kind` is
117
+ * kept as `string` here — the narrowing to `GenericComposeErrorKind` happens in
118
+ * `errorFromHttpResponse`, where the value is lifted onto `ComposeError`.
119
+ */
120
+ export interface ServerFailedPreparedOp {
121
+ readonly callId: string;
122
+ readonly op: string;
123
+ readonly kind: string;
124
+ readonly message: string;
125
+ readonly path?: string;
126
+ }
127
+
115
128
  /** Error envelope returned on non-2xx responses. */
116
129
  export interface ServerErrorBody {
117
130
  readonly error?: {
@@ -119,12 +132,26 @@ export interface ServerErrorBody {
119
132
  readonly message?: string;
120
133
  readonly path?: string;
121
134
  readonly details?: SimulationRevert;
135
+ readonly failedOps?: readonly ServerFailedPreparedOp[];
136
+ readonly succeededOps?: readonly string[];
122
137
  };
123
138
  }
124
139
 
140
+ const failedPreparedOpSchema = z.object({
141
+ callId: z.string(),
142
+ op: z.string(),
143
+ kind: z.string(),
144
+ message: z.string(),
145
+ path: z.string().optional(),
146
+ });
147
+
125
148
  // `details` is passed through (`z.custom`) rather than parsed: the SDK does not
126
149
  // own the `SimulationRevert` schema and forwards the field verbatim onto
127
150
  // `ComposeError`.
151
+ //
152
+ // `failedOps`/`succeededOps` are optional here because every error kind shares
153
+ // this envelope; they are only populated for `preparation_error` (422). Zod
154
+ // strips undeclared keys, so omitting them would silently drop them.
128
155
  const serverErrorBodySchema = z.object({
129
156
  error: z
130
157
  .object({
@@ -132,6 +159,8 @@ const serverErrorBodySchema = z.object({
132
159
  message: z.string().optional(),
133
160
  path: z.string().optional(),
134
161
  details: z.custom<SimulationRevert>().optional(),
162
+ failedOps: z.array(failedPreparedOpSchema).readonly().optional(),
163
+ succeededOps: z.array(z.string()).readonly().optional(),
135
164
  })
136
165
  .optional(),
137
166
  });