@gearbox-protocol/sdk 16.0.0-next.42 → 16.0.0-next.43

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 (56) hide show
  1. package/dist/cjs/model/index.js +4 -0
  2. package/dist/cjs/model/result.js +27 -0
  3. package/dist/cjs/onchain/accounts/intents/tail.js +1 -1
  4. package/dist/cjs/onchain/accounts/withdrawal-compressor/errors.js +14 -14
  5. package/dist/cjs/onchain/index.js +0 -1
  6. package/dist/cjs/onchain/market/zapper/errors.js +13 -12
  7. package/dist/cjs/onchain/validation/index.js +0 -1
  8. package/dist/cjs/preview/index.js +4 -1
  9. package/dist/cjs/preview/parse/errors.js +25 -22
  10. package/dist/cjs/preview/preview/errors.js +12 -11
  11. package/dist/cjs/preview/preview/previewOperation.js +41 -10
  12. package/dist/cjs/preview/simulate/errors.js +18 -17
  13. package/dist/cjs/sdk/execute/ExecuteApi.js +1 -1
  14. package/dist/cjs/sdk/index.js +1 -1
  15. package/dist/cjs/sdk/prepare/PrepareApi.js +198 -278
  16. package/dist/cjs/sdk/prepare/errors.js +5 -2
  17. package/dist/cjs/sdk/prepare/index.js +1 -1
  18. package/dist/esm/model/index.js +2 -1
  19. package/dist/esm/model/result.js +24 -0
  20. package/dist/esm/onchain/accounts/intents/tail.js +1 -1
  21. package/dist/esm/onchain/accounts/withdrawal-compressor/errors.js +14 -14
  22. package/dist/esm/onchain/index.js +2 -2
  23. package/dist/esm/onchain/market/zapper/errors.js +13 -12
  24. package/dist/esm/onchain/validation/index.js +2 -2
  25. package/dist/esm/preview/index.js +4 -2
  26. package/dist/esm/preview/parse/errors.js +25 -22
  27. package/dist/esm/preview/preview/errors.js +12 -11
  28. package/dist/esm/preview/preview/previewOperation.js +41 -10
  29. package/dist/esm/preview/simulate/errors.js +18 -17
  30. package/dist/esm/sdk/execute/ExecuteApi.js +1 -1
  31. package/dist/esm/sdk/index.js +2 -2
  32. package/dist/esm/sdk/prepare/PrepareApi.js +199 -279
  33. package/dist/esm/sdk/prepare/errors.js +5 -2
  34. package/dist/esm/sdk/prepare/index.js +2 -2
  35. package/dist/types/model/errors.d.ts +3 -31
  36. package/dist/types/model/index.d.ts +3 -2
  37. package/dist/types/model/result.d.ts +48 -0
  38. package/dist/types/onchain/accounts/withdrawal-compressor/errors.d.ts +18 -5
  39. package/dist/types/onchain/index.d.ts +2 -2
  40. package/dist/types/onchain/market/zapper/errors.d.ts +20 -6
  41. package/dist/types/onchain/validation/index.d.ts +2 -2
  42. package/dist/types/preview/index.d.ts +5 -3
  43. package/dist/types/preview/parse/errors.d.ts +36 -11
  44. package/dist/types/preview/preview/errors.d.ts +17 -5
  45. package/dist/types/preview/preview/index.d.ts +2 -2
  46. package/dist/types/preview/preview/previewOperation.d.ts +21 -2
  47. package/dist/types/preview/simulate/errors.d.ts +21 -8
  48. package/dist/types/sdk/execute/types.d.ts +6 -11
  49. package/dist/types/sdk/index.d.ts +3 -3
  50. package/dist/types/sdk/prepare/PrepareApi.d.ts +29 -26
  51. package/dist/types/sdk/prepare/errors.d.ts +56 -19
  52. package/dist/types/sdk/prepare/index.d.ts +3 -3
  53. package/dist/types/sdk/prepare/types.d.ts +73 -74
  54. package/dist/types/sdk/preview/PreviewNamespace.d.ts +4 -1
  55. package/dist/types/sdk/preview/types.d.ts +7 -3
  56. package/package.json +11 -2
@@ -1,10 +1,12 @@
1
1
  import { hexEq } from "../../onchain/utils/hex.js";
2
2
  import { MultichainConstruct } from "../../onchain/base/MultichainConstruct.js";
3
+ import { sdkErr, sdkOk } from "../../model/result.js";
4
+ import "../../model/index.js";
3
5
  import { toToken } from "../../onchain/validation/token.js";
4
6
  import { toCreditAccountSlice } from "../../onchain/accounts/intents/utils/credit-account-slice.js";
5
7
  import { CreditAccountOperationsService } from "../../onchain/accounts/intents/index.js";
6
8
  import "../../onchain/index.js";
7
- import { creditAccountNotFound, noStrategyTargetCollateral, toPrepareError, unexpectedFailure } from "./errors.js";
9
+ import { creditAccountNotFound, noStrategyTargetCollateral, toRefusalError, unexpectedFailure } from "./errors.js";
8
10
  import { withdrawableCollaterals } from "./withdrawable-collaterals.js";
9
11
  //#region src/sdk/prepare/PrepareApi.ts
10
12
  /**
@@ -15,15 +17,18 @@ import { withdrawableCollaterals } from "./withdrawable-collaterals.js";
15
17
  * protocol knowledge stays in `CreditAccountOperationsService` and
16
18
  * `PoolService`.
17
19
  *
18
- * A prepared operation names one chain, so it reads through
19
- * {@link MultichainConstruct.queryChain}: there is no second source to fall back
20
- * to, hence a chain the SDK does not cover, or one that fails the read, is a
21
- * failure of the whole request rather than a thinner answer.
20
+ * A prepared operation names one chain and reads it directly: there is no
21
+ * second source to fall back to, so a chain the SDK does not cover, or one
22
+ * that fails the read, is a failure of the whole request rather than a thinner
23
+ * answer.
22
24
  *
23
- * No method here throws. Every way a preparation can fail — the market's own
24
- * refusals, the two the namespace decides itself, and anything the chain or the
25
- * engine raises — comes back described in the envelope, see {@link PrepareError}.
26
- * A caller writes one branch, not a branch and a `try`.
25
+ * No refusable async method here throws. Every way such a preparation can
26
+ * fail — the market's own refusals, the ones the namespace decides itself, and
27
+ * anything the chain or the engine raises — comes back in the failure half of
28
+ * its `SDKReturn`, under the codes that method's signature names. A caller
29
+ * writes one branch, not a branch and a `try`. The synchronous LP methods and
30
+ * the bare `max*` reads throw on bugs and lifecycle errors instead, see
31
+ * {@link IOpportunitiesPrepare}.
27
32
  **/
28
33
  var PrepareApi = class extends MultichainConstruct {
29
34
  #ensureFresh;
@@ -31,54 +36,28 @@ var PrepareApi = class extends MultichainConstruct {
31
36
  super(sdk);
32
37
  this.#ensureFresh = ensureFresh;
33
38
  }
34
- async queryChain(props) {
35
- await this.#ensureFresh?.([this.sdk.chain(props.network).chainId]);
36
- return super.queryChain(props);
37
- }
38
39
  /**
39
- * Runs a one-chain request whose answer is an envelope, describing whatever
40
- * the chain, the read or the engine throws on the way rather than letting it
41
- * escape.
42
- *
43
- * So every method below answers instead of rejecting, and a caller has one
44
- * thing to branch on. "This cannot be done" and "we could not find out" are
45
- * still told apart, by `error.code`: only the second is
46
- * `unexpectedFailure`, and only it marks the chain failed in `meta`.
40
+ * The chain an async method reads, revalidated first so the state it is
41
+ * about to weigh is no older than the SDK's freshness bar.
47
42
  **/
48
- async #answer(chainId, run) {
49
- try {
50
- return await this.queryChain({
51
- network: chainId,
52
- run
53
- });
54
- } catch (e) {
55
- return {
56
- data: {
57
- success: false,
58
- error: unexpectedFailure(e)
59
- },
60
- meta: { chains: [failedChain(chainId, e)] }
61
- };
62
- }
43
+ async #chain(chainId) {
44
+ await this.#ensureFresh?.([this.sdk.chain(chainId).chainId]);
45
+ return this.sdk.chain(chainId);
63
46
  }
64
47
  /**
65
48
  * {@inheritDoc IOpportunitiesPrepare.finalize}
66
49
  **/
67
50
  async finalize(position, params) {
68
- return this.#answer(position.chainId, async (sdk) => {
51
+ try {
52
+ const sdk = await this.#chain(position.chainId);
53
+ const at = stateBlock(sdk);
69
54
  const intent = resumable(params.intent ?? params.claimable.intent);
70
- if (!intent) return {
71
- success: false,
72
- error: toPrepareError({
73
- reason: "noRecordedIntent",
74
- detail: void 0
75
- })
76
- };
55
+ if (!intent) return sdkErr(toRefusalError({
56
+ reason: "noRecordedIntent",
57
+ detail: void 0
58
+ }));
77
59
  const creditAccount = await slice(sdk, position.creditAccount);
78
- if (!creditAccount) return {
79
- success: false,
80
- error: creditAccountNotFound(position.creditAccount)
81
- };
60
+ if (!creditAccount) return sdkErr(creditAccountNotFound(position.creditAccount));
82
61
  return planned(await service(sdk).finishIntent({
83
62
  intent,
84
63
  claimable: toClaimableWithdrawal(params.claimable),
@@ -86,27 +65,15 @@ var PrepareApi = class extends MultichainConstruct {
86
65
  sdk,
87
66
  slippage: params.slippage,
88
67
  quotaReserve: params.quotaReserve
89
- }));
90
- });
91
- }
92
- /**
93
- * {@inheritDoc IOpportunitiesPrepare.deposit}
94
- **/
95
- deposit(pool, params) {
96
- try {
97
- return this.#depositPlan(pool, params);
68
+ }), at);
98
69
  } catch (e) {
99
- return {
100
- success: false,
101
- error: unexpectedFailure(e)
102
- };
70
+ return sdkErr(unexpectedFailure(e));
103
71
  }
104
72
  }
105
73
  /**
106
- * The arithmetic behind {@link deposit}, which throws where the SDK holds
107
- * nothing for the pool or the chain it was handed.
74
+ * {@inheritDoc IOpportunitiesPrepare.deposit}
108
75
  **/
109
- #depositPlan(pool, params) {
76
+ deposit(pool, params) {
110
77
  const chain = this.sdk.chain(pool.chainId);
111
78
  const { marketRegister, pools } = chain;
112
79
  const tokenIn = params.tokenIn ?? marketRegister.findByPool(pool.pool).pool.underlying;
@@ -128,32 +95,17 @@ var PrepareApi = class extends MultichainConstruct {
128
95
  meta: pools.getDepositMetadata(pool.pool, tokenIn, tokenOut)
129
96
  });
130
97
  if (!call) return unroutable(chain, tokenIn, tokenOut);
131
- return {
132
- success: true,
133
- data: {
134
- operations: [],
135
- state,
136
- calls: call.calls
137
- }
138
- };
98
+ return sdkOk({
99
+ operations: [],
100
+ state,
101
+ calls: call.calls,
102
+ ...stateBlock(chain)
103
+ });
139
104
  }
140
105
  /**
141
106
  * {@inheritDoc IOpportunitiesPrepare.withdraw}
142
107
  **/
143
108
  withdraw(pool, params) {
144
- try {
145
- return this.#withdrawPlan(pool, params);
146
- } catch (e) {
147
- return {
148
- success: false,
149
- error: unexpectedFailure(e)
150
- };
151
- }
152
- }
153
- /**
154
- * {@inheritDoc PrepareApi.depositPlan}
155
- **/
156
- #withdrawPlan(pool, params) {
157
109
  const chain = this.sdk.chain(pool.chainId);
158
110
  const { pools } = chain;
159
111
  const tokenIn = params.tokenIn ?? pool.pool;
@@ -173,32 +125,17 @@ var PrepareApi = class extends MultichainConstruct {
173
125
  meta: pools.getWithdrawalMetadata(pool.pool, tokenIn, tokenOut),
174
126
  mode: "withdraw"
175
127
  });
176
- return {
177
- success: true,
178
- data: {
179
- operations: [],
180
- state,
181
- calls
182
- }
183
- };
128
+ return sdkOk({
129
+ operations: [],
130
+ state,
131
+ calls,
132
+ ...stateBlock(chain)
133
+ });
184
134
  }
185
135
  /**
186
136
  * {@inheritDoc IOpportunitiesPrepare.redeem}
187
137
  **/
188
138
  redeem(pool, params) {
189
- try {
190
- return this.#redeemPlan(pool, params);
191
- } catch (e) {
192
- return {
193
- success: false,
194
- error: unexpectedFailure(e)
195
- };
196
- }
197
- }
198
- /**
199
- * {@inheritDoc PrepareApi.depositPlan}
200
- **/
201
- #redeemPlan(pool, params) {
202
139
  const chain = this.sdk.chain(pool.chainId);
203
140
  const { pools } = chain;
204
141
  const tokenIn = params.tokenIn ?? pool.pool;
@@ -218,25 +155,22 @@ var PrepareApi = class extends MultichainConstruct {
218
155
  meta: pools.getWithdrawalMetadata(pool.pool, tokenIn, tokenOut),
219
156
  mode: "redeem"
220
157
  });
221
- return {
222
- success: true,
223
- data: {
224
- operations: [],
225
- state,
226
- calls
227
- }
228
- };
158
+ return sdkOk({
159
+ operations: [],
160
+ state,
161
+ calls,
162
+ ...stateBlock(chain)
163
+ });
229
164
  }
230
165
  /**
231
166
  * {@inheritDoc IOpportunitiesPrepare.openNewStrategy}
232
167
  **/
233
168
  async openNewStrategy(strategy, params) {
234
- return this.#answer(strategy.chainId, async (sdk) => {
169
+ try {
170
+ const sdk = await this.#chain(strategy.chainId);
171
+ const at = stateBlock(sdk);
235
172
  const targetToken = params.targetToken ?? sdk.marketRegister.findCreditManager(strategy.creditManager).strategyTargetCollateral;
236
- if (!targetToken) return {
237
- success: false,
238
- error: noStrategyTargetCollateral(strategy.creditManager)
239
- };
173
+ if (!targetToken) return sdkErr(noStrategyTargetCollateral(strategy.creditManager));
240
174
  return opened(await service(sdk).openStrategyIntent({
241
175
  sdk,
242
176
  creditManager: strategy.creditManager,
@@ -246,8 +180,10 @@ var PrepareApi = class extends MultichainConstruct {
246
180
  leftoverBalances: params.leftoverBalances,
247
181
  slippage: params.slippage,
248
182
  quotaReserve: params.quotaReserve
249
- }));
250
- });
183
+ }), at);
184
+ } catch (e) {
185
+ return sdkErr(unexpectedFailure(e));
186
+ }
251
187
  }
252
188
  /**
253
189
  * {@inheritDoc IOpportunitiesPrepare.depositStrategy}
@@ -278,19 +214,11 @@ var PrepareApi = class extends MultichainConstruct {
278
214
  * {@inheritDoc IOpportunitiesPrepare.maxWithdraw}
279
215
  **/
280
216
  async maxWithdraw(position) {
281
- return this.#answer(position.chainId, async (sdk) => {
282
- const creditAccount = await slice(sdk, position.creditAccount);
283
- if (!creditAccount) return {
284
- success: false,
285
- error: creditAccountNotFound(position.creditAccount)
286
- };
287
- return {
288
- success: true,
289
- data: await service(sdk).maxWithdraw({
290
- creditAccount,
291
- sdk
292
- })
293
- };
217
+ const sdk = await this.#chain(position.chainId);
218
+ const creditAccount = await this.#account(sdk, position);
219
+ return service(sdk).maxWithdraw({
220
+ creditAccount,
221
+ sdk
294
222
  });
295
223
  }
296
224
  /**
@@ -308,19 +236,11 @@ var PrepareApi = class extends MultichainConstruct {
308
236
  * {@inheritDoc IOpportunitiesPrepare.maxRepay}
309
237
  **/
310
238
  async maxRepay(position) {
311
- return this.#answer(position.chainId, async (sdk) => {
312
- const creditAccount = await slice(sdk, position.creditAccount);
313
- if (!creditAccount) return {
314
- success: false,
315
- error: creditAccountNotFound(position.creditAccount)
316
- };
317
- return {
318
- success: true,
319
- data: await service(sdk).maxRepay({
320
- creditAccount,
321
- sdk
322
- })
323
- };
239
+ const sdk = await this.#chain(position.chainId);
240
+ const creditAccount = await this.#account(sdk, position);
241
+ return service(sdk).maxRepay({
242
+ creditAccount,
243
+ sdk
324
244
  });
325
245
  }
326
246
  /**
@@ -377,75 +297,92 @@ var PrepareApi = class extends MultichainConstruct {
377
297
  * {@inheritDoc IOpportunitiesPrepare.maxWithdrawCollateral}
378
298
  **/
379
299
  async maxWithdrawCollateral(position, token, targetHF) {
380
- return this.#answer(position.chainId, async (sdk) => {
381
- const creditAccount = await slice(sdk, position.creditAccount);
382
- if (!creditAccount) return {
383
- success: false,
384
- error: creditAccountNotFound(position.creditAccount)
385
- };
386
- return {
387
- success: true,
388
- data: await service(sdk).maxWithdrawCollateral({
389
- creditAccount,
390
- sdk,
391
- token,
392
- targetHF
393
- })
394
- };
300
+ const sdk = await this.#chain(position.chainId);
301
+ const creditAccount = await this.#account(sdk, position);
302
+ return service(sdk).maxWithdrawCollateral({
303
+ creditAccount,
304
+ sdk,
305
+ token,
306
+ targetHF
395
307
  });
396
308
  }
397
309
  /**
398
- * Shared path of the two flows that sell a position asset, and therefore have
399
- * two routes to offer: one account read, one intent, both routes quoted.
310
+ * The account a bare `max*` read weighs. These reads answer a number, not
311
+ * an envelope, so an account the markets do not hold is thrown rather than
312
+ * described, see {@link IOpportunitiesPrepare.maxWithdraw}.
313
+ **/
314
+ async #account(sdk, position) {
315
+ const creditAccount = await slice(sdk, position.creditAccount);
316
+ if (!creditAccount) throw new Error(creditAccountNotFound(position.creditAccount).message);
317
+ return creditAccount;
318
+ }
319
+ /**
320
+ * Shared path of the two flows that sell a position asset, and therefore
321
+ * have two routes to offer: one account read, one intent, both routes
322
+ * quoted. Every refusal, the crash wrap included, carries `refused` so a
323
+ * form can say which routes were ruled out and why.
324
+ *
325
+ * @typeParam X - The codes the calling flow can raise beyond the shared
326
+ * ones, per the engine trace its signature spells out.
400
327
  **/
401
328
  async #startRoutes(position, options, intent) {
402
329
  try {
403
- return await this.queryChain({
404
- network: position.chainId,
405
- run: async (sdk) => {
406
- const creditAccount = await slice(sdk, position.creditAccount);
407
- if (!creditAccount) return neitherRoute(creditAccountNotFound(position.creditAccount));
408
- return routed(await service(sdk).intentRoutes({
409
- intent,
410
- creditAccount,
411
- sdk,
412
- slippage: options.slippage,
413
- quotaReserve: options.quotaReserve
414
- }));
415
- }
416
- });
330
+ const sdk = await this.#chain(position.chainId);
331
+ const at = stateBlock(sdk);
332
+ const creditAccount = await slice(sdk, position.creditAccount);
333
+ if (!creditAccount) return neitherRoute(creditAccountNotFound(position.creditAccount));
334
+ return routed(await service(sdk).intentRoutes({
335
+ intent,
336
+ creditAccount,
337
+ sdk,
338
+ slippage: options.slippage,
339
+ quotaReserve: options.quotaReserve
340
+ }), at);
417
341
  } catch (e) {
418
- return {
419
- data: neitherRoute(unexpectedFailure(e)),
420
- meta: { chains: [failedChain(position.chainId, e)] }
421
- };
342
+ return neitherRoute(unexpectedFailure(e));
422
343
  }
423
344
  }
424
345
  /**
425
346
  * Shared path of the five flows that act on an existing account: read the
426
- * account, then run the intent through the engine.
347
+ * account, then run the intent through the engine. Whatever the chain or
348
+ * the engine throws on the way is described as `unexpectedFailure` instead
349
+ * of escaping, so the flow always answers.
350
+ *
351
+ * @typeParam X - The codes the calling flow can raise beyond the shared
352
+ * ones, per the engine trace its signature spells out.
427
353
  **/
428
354
  async #startIntent(position, options, intent) {
429
- return this.#answer(position.chainId, async (sdk) => {
355
+ try {
356
+ const sdk = await this.#chain(position.chainId);
357
+ const at = stateBlock(sdk);
430
358
  const creditAccount = await slice(sdk, position.creditAccount);
431
- if (!creditAccount) return {
432
- success: false,
433
- error: creditAccountNotFound(position.creditAccount)
434
- };
359
+ if (!creditAccount) return sdkErr(creditAccountNotFound(position.creditAccount));
435
360
  return planned(await service(sdk).startIntent({
436
361
  intent,
437
362
  creditAccount,
438
363
  sdk,
439
364
  slippage: options.slippage,
440
365
  quotaReserve: options.quotaReserve
441
- }));
442
- });
366
+ }), at);
367
+ } catch (e) {
368
+ return sdkErr(unexpectedFailure(e));
369
+ }
443
370
  }
444
371
  };
445
372
  function service(sdk) {
446
373
  return new CreditAccountOperationsService(sdk);
447
374
  }
448
375
  /**
376
+ * Reads the block the SDK's loaded state stands at — the same state every
377
+ * preparation below is computed from, so it is the block the result reports.
378
+ **/
379
+ function stateBlock(sdk) {
380
+ return {
381
+ blockNumber: Number(sdk.currentBlock),
382
+ timestamp: Number(sdk.timestamp)
383
+ };
384
+ }
385
+ /**
449
386
  * The account the request names, or nothing where the markets this SDK is
450
387
  * connected to hold no such account — closed since it was listed, or named on
451
388
  * the wrong chain. Read rather than thrown, so the caller gets a code for it.
@@ -485,89 +422,59 @@ function toClaimableWithdrawal(claimable) {
485
422
  };
486
423
  }
487
424
  /**
425
+ * The engine's refusal as one flow's failure half.
426
+ *
427
+ * The engine answers with the open union of every reason it can raise
428
+ * anywhere; which of them a given flow actually reaches is the engine trace
429
+ * written into the public signatures. This cast is the one place the open
430
+ * union is narrowed onto them — a code added to or removed from a flow has to
431
+ * move its signature, which `types.test-d.ts` pins.
432
+ **/
433
+ function refusal(issue) {
434
+ return sdkErr(toRefusalError(issue));
435
+ }
436
+ /**
488
437
  * A flow with two routes, refused before either could be quoted: the error is
489
438
  * the same one any other flow would report, with nothing to say about the
490
439
  * routes because neither was reached.
491
440
  **/
492
441
  function neitherRoute(error) {
493
- return {
494
- success: false,
495
- error: {
496
- ...error,
497
- refused: {
498
- instant: void 0,
499
- delayed: void 0
500
- }
442
+ return sdkErr({
443
+ ...error,
444
+ refused: {
445
+ instant: void 0,
446
+ delayed: void 0
501
447
  }
502
- };
503
- }
504
- /**
505
- * The chain entry for a request that was answered with
506
- * {@link UnexpectedFailureError}: the read did not happen, so the metadata says
507
- * so rather than reporting a block it never got.
508
- **/
509
- function failedChain(chainId, error) {
510
- return {
511
- chainId,
512
- status: "error",
513
- source: "onchain",
514
- error
515
- };
516
- }
517
- /**
518
- * A pool route the market does not offer, as the refusal a caller reads.
519
- *
520
- * `to` is absent where {@link lpRoute} found no output to name at all, which
521
- * is the usual way of it; both are present where a pair exists but nothing of
522
- * ours implements it.
523
- **/
524
- function unroutable(sdk, from, to) {
525
- return {
526
- success: false,
527
- error: toPrepareError({
528
- reason: "unsupportedTokenPair",
529
- detail: {
530
- from: toToken(sdk, from),
531
- to: to === void 0 ? void 0 : toToken(sdk, to)
532
- }
533
- })
534
- };
448
+ });
535
449
  }
536
450
  /**
537
451
  * The engine's answer, as the envelope the namespace speaks in: what the
538
- * operation comes to under `data`, or the refusal as an error carrying its own
539
- * numbers, see {@link toPrepareError}.
452
+ * operation comes to under `data`, stamped with the block it was computed
453
+ * from, or the refusal as an error carrying its own numbers, see
454
+ * {@link refusal}.
540
455
  *
541
456
  * The engine keeps its `ok` union — it is the shape the planners, the guards
542
457
  * and their tests are written against — and the boundary is the one place the
543
458
  * two vocabularies meet.
544
459
  **/
545
- function planned(result) {
546
- if (!result.ok) return {
547
- success: false,
548
- error: toPrepareError(result)
549
- };
460
+ function planned(result, at) {
461
+ if (!result.ok) return refusal(result);
550
462
  const { operations, state, calls } = result;
551
- return {
552
- success: true,
553
- data: {
554
- operations,
555
- state,
556
- calls
557
- }
558
- };
463
+ return sdkOk({
464
+ operations,
465
+ state,
466
+ calls,
467
+ ...at
468
+ });
559
469
  }
560
470
  /**
561
471
  * {@inheritDoc planned}
562
472
  **/
563
- function opened(result) {
564
- return result.ok ? {
565
- success: true,
566
- data: { state: result.state }
567
- } : {
568
- success: false,
569
- error: toPrepareError(result)
570
- };
473
+ function opened(result, at) {
474
+ return result.ok ? sdkOk({
475
+ state: result.state,
476
+ ...at
477
+ }) : refusal(result);
571
478
  }
572
479
  /**
573
480
  * {@inheritDoc planned}
@@ -576,40 +483,53 @@ function opened(result) {
576
483
  * missing, and it stays on the error when neither route answered, since that is
577
484
  * the same question asked of a request that has no viable half at all.
578
485
  **/
579
- function routed(result) {
486
+ function routed(result, at) {
580
487
  if (!result.ok) {
581
488
  const { refused, ...issue } = result;
582
- return {
583
- success: false,
584
- error: {
585
- ...toPrepareError(issue),
586
- refused
587
- }
588
- };
489
+ return sdkErr({
490
+ ...toRefusalError(issue),
491
+ refused
492
+ });
589
493
  }
590
494
  const { instant, delayed, refused } = result;
591
- return {
592
- success: true,
593
- data: {
594
- instant: instant && {
595
- operations: instant.operations,
596
- state: instant.state,
597
- calls: instant.calls
598
- },
599
- delayed: delayed && {
600
- operations: delayed.operations,
601
- state: delayed.state,
602
- calls: delayed.calls,
603
- delayed: delayed.delayed
604
- },
605
- refused
495
+ return sdkOk({
496
+ instant: instant && {
497
+ operations: instant.operations,
498
+ state: instant.state,
499
+ calls: instant.calls,
500
+ ...at
501
+ },
502
+ delayed: delayed && {
503
+ operations: delayed.operations,
504
+ state: delayed.state,
505
+ calls: delayed.calls,
506
+ delayed: delayed.delayed,
507
+ ...at
508
+ },
509
+ refused,
510
+ ...at
511
+ });
512
+ }
513
+ /**
514
+ * A pool route the market does not offer, as the refusal a caller reads.
515
+ *
516
+ * `to` is absent where {@link lpRoute} found no output to name at all, which
517
+ * is the usual way of it; both are present where a pair exists but nothing of
518
+ * ours implements it.
519
+ **/
520
+ function unroutable(sdk, from, to) {
521
+ return sdkErr(toRefusalError({
522
+ reason: "unsupportedTokenPair",
523
+ detail: {
524
+ from: toToken(sdk, from),
525
+ to: to === void 0 ? void 0 : toToken(sdk, to)
606
526
  }
607
- };
527
+ }));
608
528
  }
609
529
  /**
610
530
  * Picks the route the operation takes out of `tokenIn`, as a value rather than
611
531
  * an exception: an unroutable or ambiguous pair is a request the caller can
612
- * fix, so it belongs in the `ok: false` half alongside the strategy refusals.
532
+ * fix, so it belongs in the failure half alongside the strategy refusals.
613
533
  *
614
534
  * A pool with no route out of the input reports it by throwing, hence the
615
535
  * catch; a requested output is checked against the list rather than trusted,
@@ -34,8 +34,11 @@ const MESSAGES = {
34
34
  * {@link MESSAGES}. A malformed transaction is spelled out rather than spread,
35
35
  * because its detail names a `code` and a `message` of its own and they are not
36
36
  * the envelope's.
37
+ *
38
+ * Generic over the issue it is handed, so a call site that already knows the
39
+ * reason gets that reason's error back rather than a union to narrow again.
37
40
  **/
38
- function toPrepareError(issue) {
41
+ function toRefusalError(issue) {
39
42
  if (issue.reason === "malformedTransaction") return {
40
43
  code: "malformedTransaction",
41
44
  message: MESSAGES.malformedTransaction,
@@ -83,4 +86,4 @@ function unexpectedFailure(thrown) {
83
86
  };
84
87
  }
85
88
  //#endregion
86
- export { creditAccountNotFound, noStrategyTargetCollateral, toPrepareError, unexpectedFailure };
89
+ export { creditAccountNotFound, noStrategyTargetCollateral, toRefusalError, unexpectedFailure };
@@ -1,5 +1,5 @@
1
1
  import { IntentPreviewError, raise, refuse } from "../../onchain/validation/refusal.js";
2
- import { creditAccountNotFound, noStrategyTargetCollateral, toPrepareError, unexpectedFailure } from "./errors.js";
2
+ import { creditAccountNotFound, noStrategyTargetCollateral, toRefusalError, unexpectedFailure } from "./errors.js";
3
3
  import { PrepareApi } from "./PrepareApi.js";
4
4
  import "./types.js";
5
- export { IntentPreviewError, PrepareApi, creditAccountNotFound, noStrategyTargetCollateral, raise, refuse, toPrepareError, unexpectedFailure };
5
+ export { IntentPreviewError, PrepareApi, creditAccountNotFound, noStrategyTargetCollateral, raise, refuse, toRefusalError, unexpectedFailure };