@cardano-slips/flow 0.1.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 (113) 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/balance-error.d.ts +48 -0
  7. package/dist/balance-error.d.ts.map +1 -0
  8. package/dist/balance-error.js +45 -0
  9. package/dist/balance-error.js.map +1 -0
  10. package/dist/balance.d.ts +54 -0
  11. package/dist/balance.d.ts.map +1 -0
  12. package/dist/balance.js +175 -0
  13. package/dist/balance.js.map +1 -0
  14. package/dist/card.css +492 -0
  15. package/dist/card.d.ts +41 -0
  16. package/dist/card.d.ts.map +1 -0
  17. package/dist/card.js +69 -0
  18. package/dist/card.js.map +1 -0
  19. package/dist/cip30.d.ts +10 -0
  20. package/dist/cip30.d.ts.map +1 -1
  21. package/dist/cip30.js +16 -0
  22. package/dist/cip30.js.map +1 -1
  23. package/dist/complete-error.d.ts +57 -0
  24. package/dist/complete-error.d.ts.map +1 -0
  25. package/dist/complete-error.js +52 -0
  26. package/dist/complete-error.js.map +1 -0
  27. package/dist/complete.d.ts +77 -0
  28. package/dist/complete.d.ts.map +1 -0
  29. package/dist/complete.js +182 -0
  30. package/dist/complete.js.map +1 -0
  31. package/dist/derived.d.ts +8 -0
  32. package/dist/derived.d.ts.map +1 -0
  33. package/dist/derived.js +2 -0
  34. package/dist/derived.js.map +1 -0
  35. package/dist/effects-panel.d.ts +37 -0
  36. package/dist/effects-panel.d.ts.map +1 -0
  37. package/dist/effects-panel.js +64 -0
  38. package/dist/effects-panel.js.map +1 -0
  39. package/dist/effects.css +629 -0
  40. package/dist/exchange.d.ts +54 -0
  41. package/dist/exchange.d.ts.map +1 -0
  42. package/dist/exchange.js +136 -0
  43. package/dist/exchange.js.map +1 -0
  44. package/dist/explain.d.ts +39 -0
  45. package/dist/explain.d.ts.map +1 -0
  46. package/dist/explain.js +172 -0
  47. package/dist/explain.js.map +1 -0
  48. package/dist/index.d.ts +34 -3
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +19 -2
  51. package/dist/index.js.map +1 -1
  52. package/dist/ledger.d.ts +74 -0
  53. package/dist/ledger.d.ts.map +1 -0
  54. package/dist/ledger.js +402 -0
  55. package/dist/ledger.js.map +1 -0
  56. package/dist/outcome-panel.d.ts +63 -0
  57. package/dist/outcome-panel.d.ts.map +1 -0
  58. package/dist/outcome-panel.js +40 -0
  59. package/dist/outcome-panel.js.map +1 -0
  60. package/dist/parameter-form.d.ts +26 -0
  61. package/dist/parameter-form.d.ts.map +1 -0
  62. package/dist/parameter-form.js +42 -0
  63. package/dist/parameter-form.js.map +1 -0
  64. package/dist/resolve.d.ts +11 -0
  65. package/dist/resolve.d.ts.map +1 -0
  66. package/dist/resolve.js +21 -0
  67. package/dist/resolve.js.map +1 -0
  68. package/dist/sign-error.d.ts +62 -0
  69. package/dist/sign-error.d.ts.map +1 -0
  70. package/dist/sign-error.js +42 -0
  71. package/dist/sign-error.js.map +1 -0
  72. package/dist/sign.d.ts +38 -0
  73. package/dist/sign.d.ts.map +1 -0
  74. package/dist/sign.js +79 -0
  75. package/dist/sign.js.map +1 -0
  76. package/dist/slip-card.d.ts +55 -0
  77. package/dist/slip-card.d.ts.map +1 -0
  78. package/dist/slip-card.js +89 -0
  79. package/dist/slip-card.js.map +1 -0
  80. package/dist/tokens.css +97 -123
  81. package/dist/utxo.d.ts +16 -0
  82. package/dist/utxo.d.ts.map +1 -0
  83. package/dist/utxo.js +51 -0
  84. package/dist/utxo.js.map +1 -0
  85. package/dist/witness.d.ts +23 -0
  86. package/dist/witness.d.ts.map +1 -0
  87. package/dist/witness.js +101 -0
  88. package/dist/witness.js.map +1 -0
  89. package/package.json +13 -6
  90. package/src/address.ts +53 -0
  91. package/src/balance-error.ts +77 -0
  92. package/src/balance.ts +285 -0
  93. package/src/card.css +492 -0
  94. package/src/card.ts +86 -0
  95. package/src/cip30.ts +19 -0
  96. package/src/complete-error.ts +95 -0
  97. package/src/complete.ts +307 -0
  98. package/src/derived.ts +8 -0
  99. package/src/effects-panel.tsx +386 -0
  100. package/src/effects.css +629 -0
  101. package/src/exchange.ts +212 -0
  102. package/src/explain.ts +211 -0
  103. package/src/index.ts +64 -3
  104. package/src/ledger.ts +503 -0
  105. package/src/outcome-panel.tsx +263 -0
  106. package/src/parameter-form.tsx +138 -0
  107. package/src/resolve.ts +40 -0
  108. package/src/sign-error.ts +89 -0
  109. package/src/sign.ts +118 -0
  110. package/src/slip-card.tsx +244 -0
  111. package/src/tokens.css +97 -123
  112. package/src/utxo.ts +70 -0
  113. package/src/witness.ts +132 -0
package/dist/tokens.css CHANGED
@@ -2,114 +2,145 @@
2
2
  * Every colour, type role, step of spacing and radius the client uses, taken
3
3
  * from `1 · Tokens` on the design sheet. This is the only file in the package
4
4
  * that writes a colour — `test/no-hard-coded-colours.test.ts` fails if another
5
- * one does, and `test/tokens.test.ts` re-runs the sheet's contrast audit
6
- * against these values.
5
+ * one does, and `test/tokens.test.ts` re-runs the contrast audit against these
6
+ * values rather than trusting the numbers printed on the sheet.
7
7
  *
8
8
  * Scoped to a class, never `:root`: this ships into pages we do not own, and a
9
9
  * package that writes tokens onto the document redefines whatever the host
10
10
  * already called `--ink`. Put `slip-root` on the element that wraps the Slip.
11
11
  *
12
12
  * There is no reset here and no rule that selects an element. Nothing sets a
13
- * font-family on anything — a component reads `var(--font-text)` — so a host
13
+ * font-family on anything — a component reads a `--type-*` role — so a host
14
14
  * page's own stack stays where it is until something of ours asks for a token.
15
15
  *
16
- * Three families of surface, and the difference is the point:
16
+ * One theme. The sheet collapsed an earlier light/dark pair into a single
17
+ * ground that leans light, so there is no `data-theme` and no role that means
18
+ * two things. What carries the weight instead is that the three surface
19
+ * families are genuinely different surfaces:
20
+ *
17
21
  * page / card the ordinary ground, and what lifts off it
18
- * vault-* the transaction preview, which separates itself by surface
19
- * and border rather than by being dark
20
- * dark-* code blocks and the Open Graph card, dark in both themes
22
+ * vault-* the transaction preview, on slate — the one grave surface,
23
+ * and the reason the rest can stay bright
24
+ * dark-* code blocks and the Open Graph card, fixed wherever they
25
+ * appear because neither sits on a ground we control
21
26
  */
22
27
 
23
28
  .slip-root {
24
29
  color-scheme: light;
25
30
 
26
31
  /* Surfaces. Depth is a border, never a shadow: there is no shadow token. */
27
- --page: #f6f8fe; /* never pure white */
28
- --card: #ffffff;
29
- --subtle: rgba(114, 142, 243, 0.08); /* hover, chips */
30
- --skeleton: rgba(114, 142, 243, 0.14);
31
- --border: rgba(18, 23, 33, 0.12);
32
- --divider: rgba(18, 23, 33, 0.1);
32
+ --page: #ebeef8; /* never pure white */
33
+ --card: #fcfdff;
34
+ --subtle: rgba(90, 110, 205, 0.09); /* hover, chips */
35
+ --skeleton: rgba(90, 110, 205, 0.14);
36
+ --border: rgba(20, 26, 40, 0.13);
37
+ --divider: rgba(20, 26, 40, 0.1);
33
38
 
34
39
  /* Ink */
35
- --ink: #121721;
36
- --muted: #5c6a9c;
40
+ --ink: #141a26;
41
+ --muted: #56628a;
37
42
 
38
43
  /*
39
- * The split rule: --accent-fill reaches 3.05:1 on the card and is a fill —
40
- * it never carries text below 24px. An accent-coloured word uses
41
- * --accent-text. It stays a fill in the dark theme too, where it would
42
- * otherwise clear AA, because the rule is about what the colour is for.
44
+ * The split rule: --accent-fill is a fill and never carries text below 24px.
45
+ * An accent-coloured word uses --accent-text. It clears AA on the vault and
46
+ * is still held to fills there, because the rule is about what the colour is
47
+ * for and not about the ratio it happens to reach.
43
48
  */
44
49
  --accent-fill: #728ef3;
45
- --accent-text: #2f4bc4; /* links */
46
- --accent-action: #4361e8; /* button background */
47
- --accent-hover: #2f4bc4;
50
+ --accent-text: #123cd3; /* links */
51
+ --accent-action: #123cd3; /* the outline and the label of a button */
52
+ --accent-hover: #0e2fa6;
48
53
  --accent-deep: #0c288d; /* headings */
49
- --on-accent: #ffffff; /* anything drawn on --accent-action */
50
- --focus: #4361e8;
54
+ --on-accent: #ffffff; /* anything drawn on a filled accent */
55
+ --focus: #123cd3;
56
+
57
+ /* The accent lightened for slate, where #123cd3 disappears into the ground. */
58
+ --accent-on-slate: #8fa9ff;
51
59
 
52
60
  /* Semantic, and rare. A page that colours everything says nothing. */
53
- --pos: #1f7a5c; /* positive, refundable */
54
- --warn: #8c5a12;
55
- --bad: #b3261e;
56
- --bad-tint: rgba(179, 38, 30, 0.06);
57
- --bad-line: rgba(179, 38, 30, 0.24);
58
- --warn-tint: rgba(233, 180, 76, 0.16);
59
- --warn-line: rgba(233, 180, 76, 0.5);
61
+ --pos: #065708; /* value arriving */
62
+ --warn: #7a5200; /* held, taken, caution */
63
+ --bad: #d20a19; /* problem, blocked */
64
+ --bad-tint: rgba(210, 10, 25, 0.06);
65
+ --bad-line: rgba(210, 10, 25, 0.24);
66
+ --warn-tint: rgba(254, 206, 82, 0.22);
67
+ --warn-line: rgba(254, 206, 82, 0.85);
68
+ --warn-fill: #fece52; /* the warning as a fill, never as a word on a light ground */
69
+ --on-warn: #3a2600; /* words on the warning fill: the testnet bar */
60
70
 
61
71
  /* The alternating rows of the effects list, on and off. */
62
- --stripe-a: rgba(114, 142, 243, 0.16);
63
- --stripe-b: rgba(114, 142, 243, 0.05);
64
- --stripe-off-a: rgba(92, 106, 156, 0.12);
65
- --stripe-off-b: rgba(92, 106, 156, 0.04);
72
+ --stripe-a: rgba(90, 110, 205, 0.16);
73
+ --stripe-b: rgba(90, 110, 205, 0.05);
74
+ --stripe-off-a: rgba(86, 98, 138, 0.12);
75
+ --stripe-off-b: rgba(86, 98, 138, 0.04);
66
76
 
67
77
  /*
68
78
  * The transaction preview. Its own family because it is the surface that
69
- * judges the publisher, and `10 · Chrome rules` fixes it whoever hosts the
70
- * page. It follows the theme like everything else.
79
+ * judges the publisher, and `13 · Chrome rules` fixes it whoever hosts the
80
+ * page. Slate rather than black: it has to read as a distinct surface, not
81
+ * as a hole in the page.
71
82
  */
72
- --vault: #ffffff;
73
- --vault-ink: #121721;
74
- --vault-muted: #5c6a9c;
75
- --vault-surface: rgba(114, 142, 243, 0.08);
76
- --vault-border: rgba(18, 23, 33, 0.12);
77
- --vault-chip: rgba(114, 142, 243, 0.16);
78
- --vault-rule: rgba(114, 142, 243, 0.8);
79
- --vault-pos: #1f7a5c;
80
- --vault-warn: #8c5a12;
81
- --vault-bad: #b3261e;
82
- --vault-bad-tint: rgba(179, 38, 30, 0.06);
83
- --vault-bad-line: rgba(179, 38, 30, 0.32);
84
- --vault-bad-chip: rgba(179, 38, 30, 0.1);
85
- --vault-warn-tint: rgba(233, 180, 76, 0.14);
86
- --vault-warn-line: rgba(233, 180, 76, 0.55);
83
+ --vault: #222b3d;
84
+ --vault-ink: #f5f7ff;
85
+ --vault-muted: #c3cdec;
86
+ --vault-surface: rgba(255, 255, 255, 0.09);
87
+ --vault-border: rgba(255, 255, 255, 0.16);
88
+ --vault-chip: rgba(168, 188, 255, 0.22);
89
+ --vault-rule: rgba(168, 188, 255, 0.7);
90
+ --vault-pos: #6fcb72;
91
+ --vault-warn: #fece52;
92
+ /*
93
+ * #ff5247 on the sheet, lightened here: that value measures 4.42:1 on the
94
+ * vault and the sheet's own row calls it a pass. This is the colour the
95
+ * mismatch block is written in, so it clears AA or it does not ship.
96
+ */
97
+ --vault-bad: #ff5a50;
98
+ --vault-bad-tint: rgba(210, 10, 25, 0.16);
99
+ --vault-bad-line: rgba(255, 82, 71, 0.55);
100
+ --vault-bad-chip: rgba(210, 10, 25, 0.28);
101
+ --vault-warn-tint: rgba(254, 206, 82, 0.14);
102
+ --vault-warn-line: rgba(254, 206, 82, 0.55);
87
103
 
88
104
  /*
89
- * Dark in both themes, and nowhere else: code blocks in the docs and the
90
- * Open Graph card. Deliberately absent from the dark theme below — a value
91
- * that is fixed cannot also follow.
105
+ * Code blocks in the docs and the Open Graph card. Both land on a ground we
106
+ * do not control — a reader's docs theme, someone else's timeline — so they
107
+ * carry their own and do not follow anything.
92
108
  */
93
109
  --dark-bg: #121721;
94
110
  --dark-surface: rgba(255, 255, 255, 0.08);
95
111
  --dark-text: #ffffff;
96
112
  --dark-muted: #ccd6ff;
97
113
 
98
- /* Type. Each stack ends in a generic family, so a missing webfont degrades. */
99
- --font-display: Poppins, system-ui, sans-serif;
100
- --font-text: Figtree, system-ui, sans-serif;
101
- --font-mono: "IBM Plex Mono", ui-monospace, monospace;
114
+ /*
115
+ * One family, for everything. The sheet's earlier display/text/mono trio is
116
+ * gone: weight and size separate a heading from a label perfectly well, and
117
+ * three names for one face is three chances to drift apart. The stack ends in
118
+ * a generic family, so a missing webfont degrades rather than disappears.
119
+ */
120
+ --font: Poppins, system-ui, sans-serif;
102
121
 
103
122
  /* Whole roles, so half a role cannot be applied without the other half. */
104
- --type-h1: 700 44px/48px var(--font-display);
123
+ --type-h1: 700 44px/48px var(--font);
105
124
  --type-h1-tracking: -0.01em;
106
- --type-card-title: 700 18px/24px var(--font-display);
107
- --type-numeral: 700 26px/32px var(--font-display); /* a figure is always Poppins 700 */
108
- --type-body: 500 16px/24px var(--font-text);
109
- --type-button: 500 14px/20px var(--font-text);
125
+ --type-card-title: 700 18px/24px var(--font);
126
+ --type-numeral: 700 26px/32px var(--font); /* a figure is always 700 */
127
+ --type-body: 400 16px/24px var(--font);
128
+ --type-button: 500 14px/20px var(--font);
110
129
  --type-button-tracking: 0.1px;
111
- --type-label: 500 13px var(--font-text);
112
- --type-mono: 400 12px/20px var(--font-mono); /* addresses, hashes, the tx block */
130
+ --type-label: 500 13px var(--font);
131
+ /* Addresses, hashes, error codes, the tx block — text meant for a machine. */
132
+ --type-technical: 400 12px/20px var(--font);
133
+
134
+ /* The transaction preview's own scale, from `4 · Anatomy` and `6 · Preview states`. */
135
+ --type-headline: 700 32px/38px var(--font); /* the one figure the person is deciding on */
136
+ --type-preview-title: 700 20px/26px var(--font);
137
+ --type-amount: 700 20px/26px var(--font); /* a row's figure, one size so the column scans */
138
+ --type-countdown: 700 16px/20px var(--font);
139
+ --type-row: 400 14px/20px var(--font);
140
+ --type-note: 400 13px/19px var(--font);
141
+ --type-kicker: 400 11px/16px var(--font);
142
+ --type-kicker-tracking: 0.12em;
143
+ --type-chip: 600 11px/20px var(--font);
113
144
 
114
145
  /* Spacing, on a 4px base. */
115
146
  --space-4: 4px;
@@ -125,60 +156,3 @@
125
156
  --radius-control: 8px;
126
157
  --radius-card: 12px;
127
158
  }
128
-
129
- /*
130
- * The dark theme rebinds the roles rather than adding a second vocabulary. A
131
- * component writes var(--ink) once and is correct on both grounds; a component
132
- * choosing between a light token and a dark one is a component deciding the
133
- * rule, and two of them will decide it differently.
134
- *
135
- * Whether dark follows the operating system is not settled by the sheet, so it
136
- * is not decided here: the attribute is the only way in.
137
- */
138
- .slip-root[data-theme="dark"] {
139
- color-scheme: dark;
140
-
141
- --page: #121721; /* the brand dark */
142
- --card: #1b2231; /* lifted off the page */
143
- --subtle: rgba(114, 142, 243, 0.16);
144
- --skeleton: rgba(114, 142, 243, 0.18);
145
- --border: rgba(255, 255, 255, 0.14);
146
- --divider: rgba(255, 255, 255, 0.1);
147
-
148
- --ink: #ffffff;
149
- --muted: #ccd6ff;
150
-
151
- --accent-text: #a8bcff;
152
- --accent-hover: #5c79ee;
153
- --accent-deep: #a8bcff;
154
- --focus: #a8bcff;
155
-
156
- --pos: #5fd1a6;
157
- --warn: #e9b44c;
158
- --bad: #ffa39b;
159
- --bad-tint: rgba(255, 163, 155, 0.1);
160
- --bad-line: rgba(255, 163, 155, 0.4);
161
- --warn-tint: rgba(233, 180, 76, 0.14);
162
- --warn-line: rgba(233, 180, 76, 0.45);
163
-
164
- --stripe-a: rgba(114, 142, 243, 0.26);
165
- --stripe-b: rgba(114, 142, 243, 0.1);
166
- --stripe-off-a: rgba(204, 214, 255, 0.16);
167
- --stripe-off-b: rgba(204, 214, 255, 0.05);
168
-
169
- --vault: #080b12; /* still the gravest surface */
170
- --vault-ink: #ffffff;
171
- --vault-muted: #ccd6ff;
172
- --vault-surface: rgba(255, 255, 255, 0.08);
173
- --vault-border: rgba(255, 255, 255, 0.14);
174
- --vault-chip: rgba(114, 142, 243, 0.24);
175
- --vault-rule: rgba(114, 142, 243, 0.5);
176
- --vault-pos: #5fd1a6;
177
- --vault-warn: #e9b44c;
178
- --vault-bad: #ffa39b;
179
- --vault-bad-tint: rgba(255, 163, 155, 0.08);
180
- --vault-bad-line: rgba(255, 163, 155, 0.45);
181
- --vault-bad-chip: rgba(255, 163, 155, 0.2);
182
- --vault-warn-tint: rgba(233, 180, 76, 0.08);
183
- --vault-warn-line: rgba(233, 180, 76, 0.45);
184
- }
package/dist/utxo.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * CIP-30 hands unspent outputs back as CBOR hex; evolution-sdk's builder takes
3
+ * them typed. The SDK has this conversion — `cip30UtxoFromCBORHex` — but only
4
+ * behind an export path its manifest blocks, so it is the sixth gap of ADR-0004
5
+ * and belongs upstream rather than here.
6
+ */
7
+ import { UTxO } from "@evolution-sdk/evolution";
8
+ import { Either } from "effect";
9
+ import type { CborHex } from "./cip30.js";
10
+ /**
11
+ * Reads what `getUtxos` returned. `Either` and not a throw: this runs on
12
+ * whatever a browser extension chose to send, and one unreadable entry must
13
+ * not take the page down.
14
+ */
15
+ export declare const readWalletUtxos: (hexes: ReadonlyArray<CborHex>) => Either.Either<ReadonlyArray<UTxO.UTxO>, string>;
16
+ //# sourceMappingURL=utxo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utxo.d.ts","sourceRoot":"","sources":["../src/utxo.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EASL,IAAI,EACL,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE/B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAiCzC;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,aAAa,CAAC,OAAO,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,CAY7G,CAAA"}
package/dist/utxo.js ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * CIP-30 hands unspent outputs back as CBOR hex; evolution-sdk's builder takes
3
+ * them typed. The SDK has this conversion — `cip30UtxoFromCBORHex` — but only
4
+ * behind an export path its manifest blocks, so it is the sixth gap of ADR-0004
5
+ * and belongs upstream rather than here.
6
+ */
7
+ import { Address, AddressEras, Assets, CBOR, Schema, Script, TransactionInput, TransactionOutput, UTxO } from "@evolution-sdk/evolution";
8
+ import { Either } from "effect";
9
+ /** The output's address is era-tagged and a UTxO's is not; the bytes are the same. */
10
+ const toAddressStructure = (tagged) => Schema.decodeSync(Address.FromBytes)(Schema.encodeSync(AddressEras.FromBytes)(tagged));
11
+ const readOne = (hex) => {
12
+ const decoded = CBOR.fromCBORHex(hex);
13
+ if (!Array.isArray(decoded) || decoded.length !== 2) {
14
+ throw new Error("a CIP-30 unspent output is an input and an output, and this is neither");
15
+ }
16
+ const input = TransactionInput.fromCBORBytes(CBOR.toCBORBytes(decoded[0]));
17
+ const output = TransactionOutput.fromCBORBytes(CBOR.toCBORBytes(decoded[1]));
18
+ const amount = output.amount;
19
+ return new UTxO.UTxO({
20
+ transactionId: input.transactionId,
21
+ index: input.index,
22
+ address: toAddressStructure(output.address),
23
+ assets: amount._tag === "WithAssets"
24
+ ? Assets.withMultiAsset(amount.coin, amount.assets)
25
+ : Assets.fromLovelace(amount.coin),
26
+ // A Babbage output carries a datum option and a script; a Shelley one carries
27
+ // a datum hash, which is itself a valid datum option.
28
+ datumOption: output._tag === "BabbageTransactionOutput" ? output.datumOption : output.datumHash,
29
+ scriptRef: output._tag === "BabbageTransactionOutput" && output.scriptRef
30
+ ? Script.fromCBOR(output.scriptRef.bytes)
31
+ : undefined
32
+ });
33
+ };
34
+ /**
35
+ * Reads what `getUtxos` returned. `Either` and not a throw: this runs on
36
+ * whatever a browser extension chose to send, and one unreadable entry must
37
+ * not take the page down.
38
+ */
39
+ export const readWalletUtxos = (hexes) => {
40
+ const utxos = [];
41
+ for (const [index, hex] of hexes.entries()) {
42
+ try {
43
+ utxos.push(readOne(hex));
44
+ }
45
+ catch (cause) {
46
+ return Either.left(`unspent output ${index} could not be read: ${cause instanceof Error ? cause.message : String(cause)}`);
47
+ }
48
+ }
49
+ return Either.right(utxos);
50
+ };
51
+ //# sourceMappingURL=utxo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utxo.js","sourceRoot":"","sources":["../src/utxo.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,OAAO,EACP,WAAW,EACX,MAAM,EACN,IAAI,EACJ,MAAM,EACN,MAAM,EACN,gBAAgB,EAChB,iBAAiB,EACjB,IAAI,EACL,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAI/B,sFAAsF;AACtF,MAAM,kBAAkB,GAAG,CAAC,MAA+B,EAAmB,EAAE,CAC9E,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;AAExF,MAAM,OAAO,GAAG,CAAC,GAAY,EAAa,EAAE;IAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;IACrC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAA;IAC3F,CAAC;IACD,MAAM,KAAK,GAAG,gBAAgB,CAAC,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC1E,MAAM,MAAM,GAAG,iBAAiB,CAAC,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC5E,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAA;IAE5B,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC;QACnB,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,OAAO,EAAE,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC;QAC3C,MAAM,EACJ,MAAM,CAAC,IAAI,KAAK,YAAY;YAC1B,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC;YACnD,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC;QACtC,8EAA8E;QAC9E,sDAAsD;QACtD,WAAW,EAAE,MAAM,CAAC,IAAI,KAAK,0BAA0B,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS;QAC/F,SAAS,EACP,MAAM,CAAC,IAAI,KAAK,0BAA0B,IAAI,MAAM,CAAC,SAAS;YAC5D,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;YACzC,CAAC,CAAC,SAAS;KAChB,CAAC,CAAA;AACJ,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,KAA6B,EAAmD,EAAE;IAChH,MAAM,KAAK,GAAqB,EAAE,CAAA;IAClC,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3C,IAAI,CAAC;YACH,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;QAC1B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,MAAM,CAAC,IAAI,CAChB,kBAAkB,KAAK,uBAAuB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACvG,CAAA;QACH,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;AAC5B,CAAC,CAAA"}
@@ -0,0 +1,23 @@
1
+ import { Effect } from "effect";
2
+ import type { CborHex } from "./cip30.js";
3
+ import { type SigningError } from "./sign-error.js";
4
+ /**
5
+ * BLAKE2b-256 over the body's original bytes, never a re-encode: this is the id
6
+ * the chain computes, and it is CIP-0186's commit — what binds a returned
7
+ * witness set to the body whose effects a person was shown.
8
+ */
9
+ export declare const transactionIdOf: (transaction: CborHex) => Effect.Effect<string, SigningError>;
10
+ /**
11
+ * Refuses unless this is the body the effects were derived from. Asked twice —
12
+ * before the wallet is given anything, and again as assembly begins — because
13
+ * either moment is one where the transaction can stop being the one on screen.
14
+ */
15
+ export declare const requireSameBody: (transaction: CborHex, transactionId: string) => Effect.Effect<void, SigningError>;
16
+ /**
17
+ * Appends a returned witness set to the body it was asked for, or says why not.
18
+ * `transactionId` is the id the effects were derived from: witnesses go into
19
+ * that body or into none, which is what makes what a person was shown the thing
20
+ * they signed.
21
+ */
22
+ export declare const assembleWitnesses: (transaction: CborHex, witnesses: CborHex, transactionId: string) => Effect.Effect<CborHex, SigningError>;
23
+ //# sourceMappingURL=witness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"witness.d.ts","sourceRoot":"","sources":["../src/witness.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAA;AAEvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AACzC,OAAO,EAAU,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAa3D;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,aAAa,OAAO,KAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAYxF,CAAA;AA0BD;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,aAAa,OAAO,EAAE,eAAe,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAO3G,CAAA;AAEH;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAC5B,aAAa,OAAO,EACpB,WAAW,OAAO,EAClB,eAAe,MAAM,KACpB,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY,CAwClC,CAAA"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * The transaction id, and the witnesses that may go into the body it names.
3
+ * Pure: nothing here talks to a wallet, so what assembly accepts can be stated
4
+ * as a rule rather than observed against an extension.
5
+ */
6
+ import { extractTransactionBody } from "@cardano-slips/verifier";
7
+ import { Transaction, TransactionWitnessSet } from "@evolution-sdk/evolution";
8
+ import { blake2b } from "@noble/hashes/blake2.js";
9
+ import { Effect, Either } from "effect";
10
+ import { refuse } from "./sign-error.js";
11
+ const hex = /^[0-9a-fA-F]*$/;
12
+ const bytesOf = (value) => {
13
+ if (value.length % 2 !== 0 || !hex.test(value))
14
+ return undefined;
15
+ return Uint8Array.from({ length: value.length / 2 }, (_, index) => Number.parseInt(value.slice(index * 2, index * 2 + 2), 16));
16
+ };
17
+ const toHex = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
18
+ /**
19
+ * BLAKE2b-256 over the body's original bytes, never a re-encode: this is the id
20
+ * the chain computes, and it is CIP-0186's commit — what binds a returned
21
+ * witness set to the body whose effects a person was shown.
22
+ */
23
+ export const transactionIdOf = (transaction) => {
24
+ const bytes = bytesOf(transaction);
25
+ if (bytes === undefined) {
26
+ return Effect.fail(refuse("UnreadableTransaction", "This transaction is not readable as CBOR."));
27
+ }
28
+ const body = extractTransactionBody(bytes);
29
+ if (Either.isLeft(body)) {
30
+ return Effect.fail(refuse("UnreadableTransaction", `This transaction is not readable: ${body.left.message}`, { cause: body.left }));
31
+ }
32
+ return Effect.succeed(toHex(blake2b(body.right.bodyBytes, { dkLen: 32 })));
33
+ };
34
+ /**
35
+ * What a version 1 transaction is never signed with. We build nothing that
36
+ * spends from a script, so any of these in a returned set is material the body
37
+ * was not judged with, and merging it would let a wallet add to a transaction
38
+ * after the moment it was shown.
39
+ */
40
+ const unexpected = (set) => {
41
+ if ((set.nativeScripts ?? []).length > 0)
42
+ return "native scripts";
43
+ if ((set.bootstrapWitnesses ?? []).length > 0)
44
+ return "bootstrap witnesses";
45
+ if ((set.plutusV1Scripts ?? []).length > 0)
46
+ return "Plutus v1 scripts";
47
+ if ((set.plutusV2Scripts ?? []).length > 0)
48
+ return "Plutus v2 scripts";
49
+ if ((set.plutusV3Scripts ?? []).length > 0)
50
+ return "Plutus v3 scripts";
51
+ if ((set.plutusData ?? []).length > 0)
52
+ return "datums";
53
+ if (set.redeemers !== undefined)
54
+ return "redeemers";
55
+ return undefined;
56
+ };
57
+ const readWitnessSet = (witnesses) => Effect.try({
58
+ try: () => TransactionWitnessSet.fromCBORHex(witnesses),
59
+ catch: (cause) => refuse("UnreadableWitnesses", "The wallet answered with something that is not a witness set.", { cause })
60
+ });
61
+ /**
62
+ * Refuses unless this is the body the effects were derived from. Asked twice —
63
+ * before the wallet is given anything, and again as assembly begins — because
64
+ * either moment is one where the transaction can stop being the one on screen.
65
+ */
66
+ export const requireSameBody = (transaction, transactionId) => Effect.flatMap(transactionIdOf(transaction), (held) => held === transactionId
67
+ ? Effect.void
68
+ : Effect.fail(refuse("WrongBody", "This is not the transaction the effects were derived from, so it will not be signed.")));
69
+ /**
70
+ * Appends a returned witness set to the body it was asked for, or says why not.
71
+ * `transactionId` is the id the effects were derived from: witnesses go into
72
+ * that body or into none, which is what makes what a person was shown the thing
73
+ * they signed.
74
+ */
75
+ export const assembleWitnesses = (transaction, witnesses, transactionId) => Effect.gen(function* () {
76
+ yield* requireSameBody(transaction, transactionId);
77
+ const set = yield* readWitnessSet(witnesses);
78
+ const material = unexpected(set);
79
+ if (material !== undefined) {
80
+ return yield* Effect.fail(refuse("UnexpectedWitnessMaterial", `The wallet answered with ${material}, which this transaction never asked for.`));
81
+ }
82
+ // evolution-sdk returns the transaction untouched for an empty set, so
83
+ // without this an unsigned transaction would go to `submitTx` looking signed.
84
+ if ((set.vkeyWitnesses ?? []).length === 0) {
85
+ return yield* Effect.fail(refuse("NoWitnesses", "The wallet answered without a signature."));
86
+ }
87
+ // Appends to `vkeyWitnesses` and re-encodes from the format the transaction
88
+ // arrived in, so a co-signer's witness survives and the body keeps its bytes.
89
+ const assembled = yield* Effect.try({
90
+ try: () => Transaction.addVKeyWitnessesHex(transaction, witnesses),
91
+ catch: (cause) => refuse("UnreadableWitnesses", "The wallet's witnesses could not be assembled into the transaction.", { cause })
92
+ });
93
+ // The body is what the id is taken over, so an id that moved means the
94
+ // bytes moved — and the person would be signing something else.
95
+ const after = yield* transactionIdOf(assembled);
96
+ if (after !== transactionId) {
97
+ return yield* Effect.fail(refuse("WrongBody", "Assembling the signature changed the transaction, so it will not be submitted."));
98
+ }
99
+ return assembled;
100
+ });
101
+ //# sourceMappingURL=witness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"witness.js","sourceRoot":"","sources":["../src/witness.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardano-slips/flow",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "The client side of Cardano Slips: CIP-30 orchestration, local balancing, and the React components that render a Slip and block a signature when its effects disagree",
5
5
  "keywords": [
6
6
  "cardano",
@@ -18,12 +18,16 @@
18
18
  },
19
19
  "license": "MIT",
20
20
  "type": "module",
21
- "sideEffects": false,
21
+ "sideEffects": [
22
+ "*.css"
23
+ ],
22
24
  "exports": {
23
25
  ".": {
24
26
  "types": "./dist/index.d.ts",
25
27
  "default": "./dist/index.js"
26
28
  },
29
+ "./card.css": "./dist/card.css",
30
+ "./effects.css": "./dist/effects.css",
27
31
  "./tokens.css": "./dist/tokens.css",
28
32
  "./package.json": "./package.json"
29
33
  },
@@ -42,9 +46,11 @@
42
46
  "react": "^19.0.0"
43
47
  },
44
48
  "dependencies": {
49
+ "@evolution-sdk/evolution": "^0.5.13",
50
+ "@noble/hashes": "^2.0.1",
45
51
  "effect": "^3.22.1",
46
- "@cardano-slips/core": "^0.2.0",
47
- "@cardano-slips/verifier": "^0.2.0"
52
+ "@cardano-slips/core": "^0.3.0",
53
+ "@cardano-slips/verifier": "^0.4.0"
48
54
  },
49
55
  "devDependencies": {
50
56
  "@testing-library/react": "^16.3.2",
@@ -52,10 +58,11 @@
52
58
  "happy-dom": "^20.11.6",
53
59
  "react": "^19.2.8",
54
60
  "react-dom": "^19.2.8",
55
- "typescript": "^6.0.3"
61
+ "typescript": "^6.0.3",
62
+ "@cardano-slips/example-slips": "^0.0.0"
56
63
  },
57
64
  "scripts": {
58
- "build": "tsc -b tsconfig.build.json && mkdir -p dist && cp src/tokens.css dist/tokens.css",
65
+ "build": "tsc -b tsconfig.build.json && mkdir -p dist && cp src/*.css dist/",
59
66
  "typecheck": "tsc -b tsconfig.src.json && tsc -p tsconfig.test.json",
60
67
  "test": "vitest run",
61
68
  "lint": "eslint .",
package/src/address.ts CHANGED
@@ -67,3 +67,56 @@ export const readWalletAddress = (hex: string): Either.Either<WalletAddress, str
67
67
  onRight: () => Either.right({ bech32, networkId: networkId as NetworkId })
68
68
  })
69
69
  }
70
+
71
+ export type OwnAddress = {
72
+ /** Raw, as the verifier compares them against the body's addresses. */
73
+ readonly bytes: Uint8Array
74
+ /** Absent for a Byron address, whose header carries no network. */
75
+ readonly networkId?: NetworkId
76
+ }
77
+
78
+ const BYRON_TYPE = 8
79
+ const FIRST_REWARD_TYPE = 14
80
+ const LAST_REWARD_TYPE = 15
81
+
82
+ /** A header byte and one or two 28-byte hashes. */
83
+ const shelleyLengths: Readonly<Record<number, ReadonlyArray<number>>> = {
84
+ 0: [57],
85
+ 1: [57],
86
+ 2: [57],
87
+ 3: [57],
88
+ 6: [29],
89
+ 7: [29],
90
+ 14: [29],
91
+ 15: [29]
92
+ }
93
+
94
+ /**
95
+ * Reads an address the wallet calls its own: any payment address, a reward
96
+ * account, or a Byron address an old wallet still reports as used. Refusing
97
+ * the Byron one would stop the whole flow over an address nothing here spends.
98
+ */
99
+ export const readOwnAddress = (hex: unknown): Either.Either<OwnAddress, string> => {
100
+ // Typed as hex by CIP-30, and sent by an extension under no obligation to.
101
+ const raw = typeof hex === "string" ? fromHex(hex.trim()) : undefined
102
+ if (raw === undefined) return refuse(`${String(hex).slice(0, 16)}… is not hex`)
103
+
104
+ const bytes = unwrapCbor(raw)
105
+ const header = bytes[0]
106
+ const type = header >> 4
107
+ if (type === BYRON_TYPE) return Either.right({ bytes })
108
+
109
+ const networkId = header & 0x0f
110
+ if (networkId !== 0 && networkId !== 1) return refuse(`the address header names network ${networkId}`)
111
+
112
+ const payment = type <= LAST_PAYMENT_TYPE
113
+ const reward = type >= FIRST_REWARD_TYPE && type <= LAST_REWARD_TYPE
114
+ if (!payment && !reward) return refuse(`address type ${type} is not one a wallet holds`)
115
+
116
+ // Pointer addresses (4, 5) end in variable-length integers, so only a floor applies.
117
+ const lengths = shelleyLengths[type]
118
+ const fits = lengths === undefined ? bytes.length > 29 : lengths.includes(bytes.length)
119
+ if (!fits) return refuse(`${bytes.length} bytes is the wrong length for address type ${type}`)
120
+
121
+ return Either.right({ bytes, networkId: networkId as NetworkId })
122
+ }