@cardano-slips/flow 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +53 -9
  2. package/dist/address.d.ts +12 -0
  3. package/dist/address.d.ts.map +1 -1
  4. package/dist/address.js +43 -0
  5. package/dist/address.js.map +1 -1
  6. package/dist/card.css +492 -0
  7. package/dist/card.d.ts +41 -0
  8. package/dist/card.d.ts.map +1 -0
  9. package/dist/card.js +69 -0
  10. package/dist/card.js.map +1 -0
  11. package/dist/complete-error.d.ts +8 -1
  12. package/dist/complete-error.d.ts.map +1 -1
  13. package/dist/complete-error.js +6 -1
  14. package/dist/complete-error.js.map +1 -1
  15. package/dist/complete.d.ts +29 -10
  16. package/dist/complete.d.ts.map +1 -1
  17. package/dist/complete.js +83 -9
  18. package/dist/complete.js.map +1 -1
  19. package/dist/derived.d.ts +8 -0
  20. package/dist/derived.d.ts.map +1 -0
  21. package/dist/derived.js +2 -0
  22. package/dist/derived.js.map +1 -0
  23. package/dist/effects-panel.d.ts +37 -0
  24. package/dist/effects-panel.d.ts.map +1 -0
  25. package/dist/effects-panel.js +64 -0
  26. package/dist/effects-panel.js.map +1 -0
  27. package/dist/effects.css +629 -0
  28. package/dist/exchange.d.ts +54 -0
  29. package/dist/exchange.d.ts.map +1 -0
  30. package/dist/exchange.js +136 -0
  31. package/dist/exchange.js.map +1 -0
  32. package/dist/explain.d.ts +39 -0
  33. package/dist/explain.d.ts.map +1 -0
  34. package/dist/explain.js +172 -0
  35. package/dist/explain.js.map +1 -0
  36. package/dist/index.d.ts +19 -3
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +9 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/ledger.d.ts +74 -0
  41. package/dist/ledger.d.ts.map +1 -0
  42. package/dist/ledger.js +402 -0
  43. package/dist/ledger.js.map +1 -0
  44. package/dist/outcome-panel.d.ts +63 -0
  45. package/dist/outcome-panel.d.ts.map +1 -0
  46. package/dist/outcome-panel.js +40 -0
  47. package/dist/outcome-panel.js.map +1 -0
  48. package/dist/parameter-form.d.ts +26 -0
  49. package/dist/parameter-form.d.ts.map +1 -0
  50. package/dist/parameter-form.js +42 -0
  51. package/dist/parameter-form.js.map +1 -0
  52. package/dist/slip-card.d.ts +55 -0
  53. package/dist/slip-card.d.ts.map +1 -0
  54. package/dist/slip-card.js +89 -0
  55. package/dist/slip-card.js.map +1 -0
  56. package/dist/tokens.css +57 -32
  57. package/package.json +9 -5
  58. package/src/address.ts +53 -0
  59. package/src/card.css +492 -0
  60. package/src/card.ts +86 -0
  61. package/src/complete-error.ts +14 -2
  62. package/src/complete.ts +128 -19
  63. package/src/derived.ts +8 -0
  64. package/src/effects-panel.tsx +386 -0
  65. package/src/effects.css +629 -0
  66. package/src/exchange.ts +212 -0
  67. package/src/explain.ts +211 -0
  68. package/src/index.ts +39 -3
  69. package/src/ledger.ts +503 -0
  70. package/src/outcome-panel.tsx +263 -0
  71. package/src/parameter-form.tsx +138 -0
  72. package/src/slip-card.tsx +244 -0
  73. package/src/tokens.css +57 -32
package/src/card.css ADDED
@@ -0,0 +1,492 @@
1
+ /*
2
+ * The action card, drawn from `2 · Action card` on the design sheet. Optional:
3
+ * a consumer who imports only `tokens.css` gets the markup and the class names
4
+ * and styles them however they like. Nothing here writes a colour — every value
5
+ * is a token, and `test/no-hard-coded-colours.test.ts` fails if that stops
6
+ * being true.
7
+ *
8
+ * The card is the publisher's surface (`13 · Chrome rules`): its content is
9
+ * their claim, so its look is theirs to own. The chrome that judges that claim
10
+ * — the effects panel and the mismatch block — is fixed and is not styled here.
11
+ *
12
+ * Every text rule states family, size, weight and line-height together through
13
+ * a `--type-*` role, so a host page's inherited stack cannot reach in.
14
+ */
15
+
16
+ .slip-card {
17
+ box-sizing: border-box;
18
+ position: relative;
19
+
20
+ /* Widths are answered against the card's own box: it never assumes it owns the viewport. */
21
+ container-type: inline-size;
22
+
23
+ display: flex;
24
+ flex-direction: column;
25
+ gap: var(--space-16);
26
+
27
+ width: 100%;
28
+ max-width: 680px;
29
+ margin: 0;
30
+ padding: var(--space-20);
31
+
32
+ background: var(--card);
33
+ border: 1px solid var(--border);
34
+ border-radius: var(--radius-card);
35
+ }
36
+
37
+ .slip-card *,
38
+ .slip-card *::before,
39
+ .slip-card *::after {
40
+ box-sizing: border-box;
41
+ }
42
+
43
+ .slip-card__head {
44
+ display: flex;
45
+ gap: var(--space-12);
46
+ align-items: flex-start;
47
+ }
48
+
49
+ .slip-card__icon {
50
+ flex: none;
51
+ width: 48px;
52
+ height: 48px;
53
+ border: 1px solid var(--border);
54
+ border-radius: var(--radius-control);
55
+ overflow: hidden;
56
+ }
57
+
58
+ .slip-card__image,
59
+ .slip-card__pattern,
60
+ .slip-card__monogram {
61
+ display: flex;
62
+ align-items: center;
63
+ justify-content: center;
64
+ width: 100%;
65
+ height: 100%;
66
+ }
67
+
68
+ .slip-card__image {
69
+ object-fit: cover;
70
+ }
71
+
72
+ /* The two fallbacks. Neither is a generic glyph: one is the publisher's initial, the other is no claim at all. */
73
+ .slip-card__monogram {
74
+ background: var(--subtle);
75
+ color: var(--accent-text);
76
+ font: var(--type-card-title);
77
+ }
78
+
79
+ .slip-card__pattern {
80
+ background-image: repeating-linear-gradient(135deg, var(--stripe-a) 0 5px, var(--stripe-b) 5px 10px);
81
+ }
82
+
83
+ .slip-card__alarm {
84
+ display: flex;
85
+ flex: none;
86
+ align-items: center;
87
+ justify-content: center;
88
+ width: 48px;
89
+ height: 48px;
90
+ border: 1px solid var(--bad-line);
91
+ border-radius: var(--radius-control);
92
+ background: var(--bad-tint);
93
+ color: var(--bad);
94
+ font: var(--type-card-title);
95
+ }
96
+
97
+ .slip-card__identity {
98
+ display: flex;
99
+ flex-direction: column;
100
+ gap: var(--space-4);
101
+ min-width: 0;
102
+ }
103
+
104
+ .slip-card__title {
105
+ margin: 0;
106
+ color: var(--ink);
107
+ font: var(--type-card-title);
108
+ text-wrap: pretty;
109
+ }
110
+
111
+ .slip-card__origin {
112
+ color: var(--muted);
113
+ font: var(--type-technical);
114
+ overflow-wrap: anywhere;
115
+ }
116
+
117
+ .slip-card__description {
118
+ margin: 0;
119
+ max-width: 52ch;
120
+ color: var(--muted);
121
+ font: var(--type-body);
122
+ text-wrap: pretty;
123
+ }
124
+
125
+ .slip-card__note {
126
+ margin: 0;
127
+ padding-top: var(--space-12);
128
+ border-top: 1px solid var(--divider);
129
+ color: var(--muted);
130
+ font: var(--type-label);
131
+ }
132
+
133
+ .slip-card__reason {
134
+ margin: 0;
135
+ color: var(--muted);
136
+ font: var(--type-label);
137
+ text-wrap: pretty;
138
+ }
139
+
140
+ /* A closed card stays readable. Only the action is gone. */
141
+ .slip-card[data-closed] .slip-card__title {
142
+ color: var(--muted);
143
+ }
144
+
145
+ .slip-card[data-closed] .slip-card__pattern {
146
+ background-image: repeating-linear-gradient(135deg, var(--stripe-off-a) 0 5px, var(--stripe-off-b) 5px 10px);
147
+ }
148
+
149
+ .slip-card__actions {
150
+ display: flex;
151
+ flex-wrap: wrap;
152
+ gap: var(--space-8);
153
+ }
154
+
155
+ /*
156
+ * One button, outlined. The sheet drew a filled primary until the palette
157
+ * change and now draws every action the same way, so `data-primary` marks which
158
+ * one leads without claiming a look the sheet no longer gives it.
159
+ */
160
+ .slip-card__action {
161
+ flex: 0 1 auto;
162
+ height: 44px;
163
+ padding: 0 var(--space-20);
164
+ border: 1px solid var(--accent-action);
165
+ border-radius: var(--radius-control);
166
+ background: transparent;
167
+ color: var(--accent-action);
168
+ font: var(--type-button);
169
+ letter-spacing: var(--type-button-tracking);
170
+ white-space: nowrap;
171
+ cursor: pointer;
172
+ }
173
+
174
+ /* Alone, a button is the width of the card; among others it is the width of its words. */
175
+ .slip-card__action:only-child {
176
+ flex: 1 1 100%;
177
+ }
178
+
179
+ .slip-card__action:not(:disabled):hover {
180
+ border-color: var(--accent-hover);
181
+ background: var(--subtle);
182
+ color: var(--accent-hover);
183
+ }
184
+
185
+ .slip-card__action:disabled {
186
+ border-color: var(--border);
187
+ background: var(--subtle);
188
+ color: var(--muted);
189
+ cursor: not-allowed;
190
+ }
191
+
192
+ .slip-fields {
193
+ display: flex;
194
+ flex-direction: column;
195
+ gap: var(--space-16);
196
+ }
197
+
198
+ .slip-field {
199
+ display: flex;
200
+ flex-direction: column;
201
+ gap: var(--space-8);
202
+ }
203
+
204
+ .slip-field__label {
205
+ color: var(--muted);
206
+ font: var(--type-label);
207
+ }
208
+
209
+ .slip-field__control {
210
+ display: flex;
211
+ align-items: center;
212
+ gap: var(--space-8);
213
+ height: 44px;
214
+ padding: 0 var(--space-12);
215
+ border: 1px solid var(--border);
216
+ border-radius: var(--radius-control);
217
+ background: var(--card);
218
+ }
219
+
220
+ .slip-field__control:focus-within {
221
+ border-color: var(--accent-fill);
222
+ }
223
+
224
+ .slip-field[data-invalid] .slip-field__control {
225
+ border-color: var(--bad-line);
226
+ background: var(--bad-tint);
227
+ }
228
+
229
+ .slip-field__input {
230
+ flex: 1;
231
+ min-width: 0;
232
+ padding: 0;
233
+ border: none;
234
+ outline: none;
235
+ background: transparent;
236
+ color: var(--ink);
237
+ font: var(--type-body);
238
+ }
239
+
240
+ /* A figure is always Poppins 700 — the one type rule the token file states out loud. */
241
+ .slip-field__input--numeral {
242
+ font: var(--type-numeral);
243
+ }
244
+
245
+ /* The helper line: what will be accepted on the left, whether it is required on the right. */
246
+ .slip-field__help {
247
+ display: flex;
248
+ justify-content: space-between;
249
+ gap: var(--space-12);
250
+ }
251
+
252
+ .slip-field__bounds,
253
+ .slip-field__note {
254
+ color: var(--muted);
255
+ font: var(--type-label);
256
+ }
257
+
258
+ /* The error replaces the bounds rather than joining them: two lines of guidance is one too many. */
259
+ .slip-field[data-invalid] .slip-field__bounds {
260
+ color: var(--bad);
261
+ }
262
+
263
+ .slip-field__code {
264
+ color: var(--muted);
265
+ font: var(--type-technical);
266
+ }
267
+
268
+ .slip-card__action:focus-visible,
269
+ .slip-field__input:focus-visible {
270
+ outline: 2px solid var(--focus);
271
+ outline-offset: 2px;
272
+ }
273
+
274
+ .slip-card--loading {
275
+ animation: slip-pulse 1.6s ease-in-out infinite;
276
+ }
277
+
278
+ .slip-skeleton__lines {
279
+ display: flex;
280
+ flex-direction: column;
281
+ gap: var(--space-8);
282
+ }
283
+
284
+ .slip-skeleton {
285
+ display: block;
286
+ border-radius: var(--radius-chip);
287
+ background: var(--skeleton);
288
+ }
289
+
290
+ .slip-skeleton--icon {
291
+ flex: none;
292
+ width: 48px;
293
+ height: 48px;
294
+ border-radius: var(--radius-control);
295
+ }
296
+
297
+ .slip-skeleton--title {
298
+ width: 72%;
299
+ height: 18px;
300
+ }
301
+
302
+ .slip-skeleton--origin {
303
+ width: 32%;
304
+ height: 12px;
305
+ background: var(--subtle);
306
+ }
307
+
308
+ .slip-skeleton--line {
309
+ width: 100%;
310
+ height: 12px;
311
+ background: var(--subtle);
312
+ }
313
+
314
+ .slip-skeleton--short {
315
+ width: 64%;
316
+ }
317
+
318
+ .slip-skeleton--action {
319
+ height: 44px;
320
+ border-radius: var(--radius-control);
321
+ }
322
+
323
+ /* The receipt is back on the card surface: the preview's job is done, so its ground goes with it. */
324
+ .slip-receipt {
325
+ width: 100%;
326
+ max-width: 560px;
327
+ box-sizing: border-box;
328
+ padding: 24px;
329
+ display: flex;
330
+ flex-direction: column;
331
+ gap: var(--space-16);
332
+ color: var(--ink);
333
+ background: var(--card);
334
+ border: 1px solid var(--border);
335
+ border-radius: var(--radius-card);
336
+ }
337
+
338
+ .slip-receipt__head {
339
+ display: flex;
340
+ gap: 14px;
341
+ align-items: flex-start;
342
+ }
343
+
344
+ .slip-receipt__tick {
345
+ width: 48px;
346
+ height: 48px;
347
+ flex: none;
348
+ display: flex;
349
+ align-items: center;
350
+ justify-content: center;
351
+ font: var(--type-preview-title);
352
+ color: var(--pos);
353
+ background: var(--subtle);
354
+ border: 1px solid var(--border);
355
+ border-radius: var(--radius-control);
356
+ }
357
+
358
+ .slip-receipt__identity {
359
+ display: flex;
360
+ flex-direction: column;
361
+ gap: 6px;
362
+ min-width: 0;
363
+ }
364
+
365
+ .slip-receipt__title {
366
+ margin: 0;
367
+ font: var(--type-card-title);
368
+ }
369
+
370
+ .slip-receipt__note {
371
+ font: var(--type-note);
372
+ color: var(--muted);
373
+ }
374
+
375
+ .slip-receipt__ledger {
376
+ padding: 0 14px;
377
+ border: 1px solid var(--border);
378
+ border-radius: var(--radius-control);
379
+ }
380
+
381
+ .slip-receipt__row {
382
+ display: flex;
383
+ justify-content: space-between;
384
+ align-items: baseline;
385
+ gap: var(--space-16);
386
+ padding: 12px 0;
387
+ font: var(--type-row);
388
+ color: var(--muted);
389
+ }
390
+
391
+ .slip-receipt__row + .slip-receipt__row {
392
+ border-top: 1px solid var(--divider);
393
+ }
394
+
395
+ .slip-receipt__amounts {
396
+ display: flex;
397
+ flex-direction: column;
398
+ align-items: flex-end;
399
+ font: var(--type-countdown);
400
+ color: var(--ink);
401
+ }
402
+
403
+ .slip-receipt__amount[data-tone="gained"] {
404
+ color: var(--pos);
405
+ }
406
+
407
+ .slip-receipt__amount[data-tone="fee"],
408
+ .slip-receipt__amount[data-tone="held"] {
409
+ color: var(--warn);
410
+ }
411
+
412
+ .slip-receipt__transaction {
413
+ display: flex;
414
+ flex-wrap: wrap;
415
+ align-items: center;
416
+ gap: var(--space-8) var(--space-12);
417
+ }
418
+
419
+ .slip-receipt__kicker {
420
+ flex-basis: 100%;
421
+ font: var(--type-kicker);
422
+ letter-spacing: var(--type-kicker-tracking);
423
+ text-transform: uppercase;
424
+ color: var(--muted);
425
+ }
426
+
427
+ .slip-receipt__id {
428
+ font: var(--type-technical);
429
+ }
430
+
431
+ .slip-receipt__link {
432
+ font: var(--type-technical);
433
+ color: var(--accent-text);
434
+ text-decoration: none;
435
+ }
436
+
437
+ .slip-receipt__link:hover {
438
+ color: var(--accent-deep);
439
+ text-decoration: underline;
440
+ }
441
+
442
+ .slip-receipt__foot {
443
+ margin: 0;
444
+ padding-top: 12px;
445
+ border-top: 1px solid var(--divider);
446
+ }
447
+
448
+ @keyframes slip-pulse {
449
+ 0%,
450
+ 100% {
451
+ opacity: 1;
452
+ }
453
+ 50% {
454
+ opacity: 0.45;
455
+ }
456
+ }
457
+
458
+ @media (prefers-reduced-motion: reduce) {
459
+ .slip-card--loading {
460
+ animation: none;
461
+ }
462
+ }
463
+
464
+ /* Named for a screen reader, absent for everyone else. */
465
+ .slip-hidden {
466
+ position: absolute;
467
+ width: 1px;
468
+ height: 1px;
469
+ margin: -1px;
470
+ padding: 0;
471
+ overflow: hidden;
472
+ clip-path: inset(50%);
473
+ white-space: nowrap;
474
+ }
475
+
476
+ /* Below 440px the card takes the 380 layout: a smaller icon and tighter rhythm. */
477
+ @container (max-width: 440px) {
478
+ .slip-card {
479
+ gap: var(--space-12);
480
+ }
481
+
482
+ .slip-card__icon,
483
+ .slip-card__alarm,
484
+ .slip-skeleton--icon {
485
+ width: 44px;
486
+ height: 44px;
487
+ }
488
+
489
+ .slip-card__action {
490
+ flex: 1 1 100%;
491
+ }
492
+ }
package/src/card.ts ADDED
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The parts of a Slip card that need no React: which buttons a Slip asks for,
3
+ * whether each one may be pressed, and what a button says once the person's
4
+ * values are in it.
5
+ */
6
+ import {
7
+ fillLabel,
8
+ type LinkedAction,
9
+ type ParameterValues,
10
+ type Reason,
11
+ type Slip,
12
+ type ValueIssue
13
+ } from "@cardano-slips/core"
14
+
15
+ /**
16
+ * The buttons the Slip asks for. `links` absent is one button labelled `label`
17
+ * whose target is the discovery URL itself — the spec's rule, not a default of
18
+ * ours, which is why it is written once here rather than in each component.
19
+ */
20
+ export const actionsOf = (slip: Slip, discoveryUrl: string): ReadonlyArray<LinkedAction> =>
21
+ slip.links?.actions ?? [{ label: slip.label, href: discoveryUrl }]
22
+
23
+ /**
24
+ * Why this action cannot be used, or undefined while it can. The top level
25
+ * wins: an action carrying `disabled: false` under a disabled Slip stays
26
+ * closed, and is never reopened by its own field.
27
+ */
28
+ export const closureOf = (slip: Slip, action: LinkedAction): Reason | undefined => {
29
+ if (slip.disabled === true) return slip.reason
30
+ return action.disabled === true ? action.reason : undefined
31
+ }
32
+
33
+ /** The host that served the link. Undefined where the URL is not one we can read. */
34
+ export const originOf = (discoveryUrl: string): string | undefined => {
35
+ try {
36
+ return new URL(discoveryUrl).host
37
+ } catch {
38
+ return undefined
39
+ }
40
+ }
41
+
42
+ /** The publisher's initial, for an icon that did not load. Never a generic glyph. */
43
+ export const monogramOf = (discoveryUrl: string): string | undefined =>
44
+ /\p{L}|\p{N}/u.exec(originOf(discoveryUrl) ?? "")?.[0]?.toUpperCase()
45
+
46
+ /**
47
+ * Own properties only. `ParameterName` admits `constructor`, `toString` and the
48
+ * rest of `Object.prototype`, and a plain object answers for every one of them —
49
+ * so a field nobody filled would be handed a function where a string belongs.
50
+ */
51
+ export const valueOf = (values: ParameterValues, name: string): string =>
52
+ Object.hasOwn(values, name) ? values[name] : ""
53
+
54
+ /** Just the values this action asked for, so a caller is handed no answer it did not request. */
55
+ export const valuesFor = (action: LinkedAction, values: ParameterValues): ParameterValues => {
56
+ const picked: Record<string, string> = {}
57
+ for (const parameter of action.parameters ?? []) picked[parameter.name] = valueOf(values, parameter.name)
58
+ return picked
59
+ }
60
+
61
+ const spelled = ["", "one", "two", "three", "four", "five", "six", "seven", "eight"]
62
+
63
+ /**
64
+ * Runs of whitespace collapse because a label is a template: `Contribute
65
+ * {amount} {token}` with nothing typed is `Contribute`, not `Contribute `.
66
+ */
67
+ const filled = (action: LinkedAction, values: ParameterValues): string =>
68
+ fillLabel(action, values).replace(/\s+/g, " ").trim()
69
+
70
+ /**
71
+ * What the button says. A label made only of placeholders empties out, and the
72
+ * Slip's own call to action stands in rather than a blank button.
73
+ */
74
+ export const actionLabel = (slip: Slip, action: LinkedAction, values: ParameterValues): string => {
75
+ const label = filled(action, values)
76
+ return label === "" ? slip.label : label
77
+ }
78
+
79
+ /**
80
+ * The label of a button that was pressed and cannot send. It counts fields
81
+ * rather than issues: one field can fail two ways and is still one thing to fix.
82
+ */
83
+ export const blockedLabel = (issues: ReadonlyArray<ValueIssue>): string => {
84
+ const fields = new Set(issues.map((issue) => issue.name)).size
85
+ return `Fix ${spelled[fields] ?? String(fields)} ${fields === 1 ? "field" : "fields"} to continue`
86
+ }
@@ -8,17 +8,23 @@ import { type ClientErrorCode } from "@cardano-slips/core"
8
8
  import type { Reason } from "@cardano-slips/verifier"
9
9
  import { Data } from "effect"
10
10
 
11
+ import type { Derived } from "./derived.js"
12
+
11
13
  export type CompletionRefusal =
12
14
  /** The wallet reported no unspent outputs, so there is nothing to build from. */
13
15
  | "NoUtxos"
14
16
  /** What `getUtxos` answered is not something these bytes can be read as. */
15
17
  | "UnreadableUtxos"
18
+ /** The wallet would not say which addresses are its own, or named one that cannot be read. */
19
+ | "UnreadableAddresses"
16
20
  /** The effects could not be derived from the transaction, or compared with the declaration. */
17
21
  | "CannotJudge"
18
22
  /** The derived effects disagree with what the endpoint declared. Nothing signs after this. */
19
23
  | "Blocked"
20
24
  /** The caller could not put the effects in front of a person, so nothing may be signed. */
21
25
  | "NotShown"
26
+ /** The person closed the transaction without signing it. Not a fault. */
27
+ | "Cancelled"
22
28
  /** The funds kept moving, and the transaction has been rebuilt as often as it will be. */
23
29
  | "OutOfAttempts"
24
30
 
@@ -29,9 +35,11 @@ export type CompletionRefusal =
29
35
  export const completionRefusals: Readonly<Record<CompletionRefusal, true>> = {
30
36
  NoUtxos: true,
31
37
  UnreadableUtxos: true,
38
+ UnreadableAddresses: true,
32
39
  CannotJudge: true,
33
40
  Blocked: true,
34
41
  NotShown: true,
42
+ Cancelled: true,
35
43
  OutOfAttempts: true
36
44
  }
37
45
 
@@ -44,7 +52,9 @@ export const slipErrorCodeFor = (refusal: CompletionRefusal): ClientErrorCode |
44
52
  case "CannotJudge":
45
53
  return "CANNOT_BALANCE"
46
54
  case "UnreadableUtxos":
55
+ case "UnreadableAddresses":
47
56
  case "NotShown":
57
+ case "Cancelled":
48
58
  case "OutOfAttempts":
49
59
  return undefined
50
60
  }
@@ -52,13 +62,14 @@ export const slipErrorCodeFor = (refusal: CompletionRefusal): ClientErrorCode |
52
62
 
53
63
  /**
54
64
  * `reasons` is what the transaction actually does against what was declared,
55
- * for the block to render. There is no way to carry on from one — a mismatch
65
+ * and `derived` is the whole of what it does, for the block to render. There is no way to carry on from one — a mismatch
56
66
  * hard-blocks, with no override, no allowlist and no confirmation.
57
67
  */
58
68
  export class CompletionError extends Data.TaggedError("CompletionError")<{
59
69
  readonly refusal: CompletionRefusal
60
70
  readonly detail: string
61
71
  readonly reasons?: ReadonlyArray<Reason>
72
+ readonly derived?: Derived
62
73
  readonly cause?: unknown
63
74
  }> {
64
75
  override get message(): string {
@@ -73,11 +84,12 @@ export class CompletionError extends Data.TaggedError("CompletionError")<{
73
84
  export const refuse = (
74
85
  refusal: CompletionRefusal,
75
86
  detail: string,
76
- extra: { readonly reasons?: ReadonlyArray<Reason>; readonly cause?: unknown } = {}
87
+ extra: { readonly reasons?: ReadonlyArray<Reason>; readonly derived?: Derived; readonly cause?: unknown } = {}
77
88
  ): CompletionError =>
78
89
  new CompletionError({
79
90
  refusal,
80
91
  detail,
81
92
  ...(extra.reasons === undefined ? {} : { reasons: extra.reasons }),
93
+ ...(extra.derived === undefined ? {} : { derived: extra.derived }),
82
94
  ...(extra.cause === undefined ? {} : { cause: extra.cause })
83
95
  })