@cardano-slips/flow 0.1.0 → 0.2.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.
- package/dist/balance-error.d.ts +48 -0
- package/dist/balance-error.d.ts.map +1 -0
- package/dist/balance-error.js +45 -0
- package/dist/balance-error.js.map +1 -0
- package/dist/balance.d.ts +54 -0
- package/dist/balance.d.ts.map +1 -0
- package/dist/balance.js +175 -0
- package/dist/balance.js.map +1 -0
- package/dist/cip30.d.ts +10 -0
- package/dist/cip30.d.ts.map +1 -1
- package/dist/cip30.js +16 -0
- package/dist/cip30.js.map +1 -1
- package/dist/complete-error.d.ts +50 -0
- package/dist/complete-error.d.ts.map +1 -0
- package/dist/complete-error.js +47 -0
- package/dist/complete-error.js.map +1 -0
- package/dist/complete.d.ts +58 -0
- package/dist/complete.d.ts.map +1 -0
- package/dist/complete.js +108 -0
- package/dist/complete.js.map +1 -0
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/resolve.d.ts +11 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +21 -0
- package/dist/resolve.js.map +1 -0
- package/dist/sign-error.d.ts +62 -0
- package/dist/sign-error.d.ts.map +1 -0
- package/dist/sign-error.js +42 -0
- package/dist/sign-error.js.map +1 -0
- package/dist/sign.d.ts +38 -0
- package/dist/sign.d.ts.map +1 -0
- package/dist/sign.js +79 -0
- package/dist/sign.js.map +1 -0
- package/dist/tokens.css +48 -99
- package/dist/utxo.d.ts +16 -0
- package/dist/utxo.d.ts.map +1 -0
- package/dist/utxo.js +51 -0
- package/dist/utxo.js.map +1 -0
- package/dist/witness.d.ts +23 -0
- package/dist/witness.d.ts.map +1 -0
- package/dist/witness.js +101 -0
- package/dist/witness.js.map +1 -0
- package/package.json +7 -4
- package/src/balance-error.ts +77 -0
- package/src/balance.ts +285 -0
- package/src/cip30.ts +19 -0
- package/src/complete-error.ts +83 -0
- package/src/complete.ts +198 -0
- package/src/index.ts +26 -1
- package/src/resolve.ts +40 -0
- package/src/sign-error.ts +89 -0
- package/src/sign.ts +118 -0
- package/src/tokens.css +48 -99
- package/src/utxo.ts +70 -0
- package/src/witness.ts +132 -0
package/dist/tokens.css
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
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
|
|
6
|
-
*
|
|
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
|
|
@@ -13,33 +13,38 @@
|
|
|
13
13
|
* font-family on anything — a component reads `var(--font-text)` — so a host
|
|
14
14
|
* page's own stack stays where it is until something of ours asks for a token.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
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,
|
|
19
|
-
* and
|
|
20
|
-
* dark-* code blocks and the Open Graph card,
|
|
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: #
|
|
28
|
-
--card: #
|
|
29
|
-
--subtle: rgba(
|
|
30
|
-
--skeleton: rgba(
|
|
31
|
-
--border: rgba(
|
|
32
|
-
--divider: rgba(
|
|
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: #
|
|
36
|
-
--muted: #
|
|
40
|
+
--ink: #141a26;
|
|
41
|
+
--muted: #56628a;
|
|
37
42
|
|
|
38
43
|
/*
|
|
39
|
-
* The split rule: --accent-fill
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
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
50
|
--accent-text: #2f4bc4; /* links */
|
|
@@ -59,36 +64,37 @@
|
|
|
59
64
|
--warn-line: rgba(233, 180, 76, 0.5);
|
|
60
65
|
|
|
61
66
|
/* The alternating rows of the effects list, on and off. */
|
|
62
|
-
--stripe-a: rgba(
|
|
63
|
-
--stripe-b: rgba(
|
|
64
|
-
--stripe-off-a: rgba(
|
|
65
|
-
--stripe-off-b: rgba(
|
|
67
|
+
--stripe-a: rgba(90, 110, 205, 0.16);
|
|
68
|
+
--stripe-b: rgba(90, 110, 205, 0.05);
|
|
69
|
+
--stripe-off-a: rgba(86, 98, 138, 0.12);
|
|
70
|
+
--stripe-off-b: rgba(86, 98, 138, 0.04);
|
|
66
71
|
|
|
67
72
|
/*
|
|
68
73
|
* The transaction preview. Its own family because it is the surface that
|
|
69
|
-
* judges the publisher, and `
|
|
70
|
-
* page.
|
|
74
|
+
* judges the publisher, and `13 · Chrome rules` fixes it whoever hosts the
|
|
75
|
+
* page. Slate rather than black: it has to read as a distinct surface, not
|
|
76
|
+
* as a hole in the page.
|
|
71
77
|
*/
|
|
72
|
-
--vault: #
|
|
73
|
-
--vault-ink: #
|
|
74
|
-
--vault-muted: #
|
|
75
|
-
--vault-surface: rgba(
|
|
76
|
-
--vault-border: rgba(
|
|
77
|
-
--vault-chip: rgba(
|
|
78
|
-
--vault-rule: rgba(
|
|
79
|
-
--vault-pos: #
|
|
80
|
-
--vault-warn: #
|
|
81
|
-
--vault-bad: #
|
|
82
|
-
--vault-bad-tint: rgba(
|
|
83
|
-
--vault-bad-line: rgba(
|
|
84
|
-
--vault-bad-chip: rgba(
|
|
85
|
-
--vault-warn-tint: rgba(233, 180, 76, 0.
|
|
86
|
-
--vault-warn-line: rgba(233, 180, 76, 0.
|
|
78
|
+
--vault: #222b3d;
|
|
79
|
+
--vault-ink: #f5f7ff;
|
|
80
|
+
--vault-muted: #c3cdec;
|
|
81
|
+
--vault-surface: rgba(255, 255, 255, 0.09);
|
|
82
|
+
--vault-border: rgba(255, 255, 255, 0.16);
|
|
83
|
+
--vault-chip: rgba(168, 188, 255, 0.22);
|
|
84
|
+
--vault-rule: rgba(168, 188, 255, 0.7);
|
|
85
|
+
--vault-pos: #5fd1a6;
|
|
86
|
+
--vault-warn: #e9b44c;
|
|
87
|
+
--vault-bad: #ffa39b;
|
|
88
|
+
--vault-bad-tint: rgba(255, 163, 155, 0.1);
|
|
89
|
+
--vault-bad-line: rgba(255, 163, 155, 0.45);
|
|
90
|
+
--vault-bad-chip: rgba(255, 163, 155, 0.18);
|
|
91
|
+
--vault-warn-tint: rgba(233, 180, 76, 0.12);
|
|
92
|
+
--vault-warn-line: rgba(233, 180, 76, 0.5);
|
|
87
93
|
|
|
88
94
|
/*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
95
|
+
* Code blocks in the docs and the Open Graph card. Both land on a ground we
|
|
96
|
+
* do not control — a reader's docs theme, someone else's timeline — so they
|
|
97
|
+
* carry their own and do not follow anything.
|
|
92
98
|
*/
|
|
93
99
|
--dark-bg: #121721;
|
|
94
100
|
--dark-surface: rgba(255, 255, 255, 0.08);
|
|
@@ -125,60 +131,3 @@
|
|
|
125
131
|
--radius-control: 8px;
|
|
126
132
|
--radius-card: 12px;
|
|
127
133
|
}
|
|
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
|
package/dist/utxo.js.map
ADDED
|
@@ -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"}
|
package/dist/witness.js
ADDED
|
@@ -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.
|
|
3
|
+
"version": "0.2.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",
|
|
@@ -42,9 +42,11 @@
|
|
|
42
42
|
"react": "^19.0.0"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
+
"@evolution-sdk/evolution": "^0.5.13",
|
|
46
|
+
"@noble/hashes": "^2.0.1",
|
|
45
47
|
"effect": "^3.22.1",
|
|
46
|
-
"@cardano-slips/
|
|
47
|
-
"@cardano-slips/
|
|
48
|
+
"@cardano-slips/verifier": "^0.3.0",
|
|
49
|
+
"@cardano-slips/core": "^0.2.0"
|
|
48
50
|
},
|
|
49
51
|
"devDependencies": {
|
|
50
52
|
"@testing-library/react": "^16.3.2",
|
|
@@ -52,7 +54,8 @@
|
|
|
52
54
|
"happy-dom": "^20.11.6",
|
|
53
55
|
"react": "^19.2.8",
|
|
54
56
|
"react-dom": "^19.2.8",
|
|
55
|
-
"typescript": "^6.0.3"
|
|
57
|
+
"typescript": "^6.0.3",
|
|
58
|
+
"@cardano-slips/example-slips": "^0.0.0"
|
|
56
59
|
},
|
|
57
60
|
"scripts": {
|
|
58
61
|
"build": "tsc -b tsconfig.build.json && mkdir -p dist && cp src/tokens.css dist/tokens.css",
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why an intent did not become a transaction. Every refusal carries a spec
|
|
3
|
+
* error code, because each one is a failure of the exchange the spec describes
|
|
4
|
+
* rather than a state of a browser extension (`wallet-error.ts` is the other
|
|
5
|
+
* half, and deliberately has refusals that carry no code).
|
|
6
|
+
*/
|
|
7
|
+
import { type ClientErrorCode } from "@cardano-slips/core"
|
|
8
|
+
import { Data } from "effect"
|
|
9
|
+
|
|
10
|
+
export type BalanceRefusal =
|
|
11
|
+
/** `validUntil` has already passed. A fresh POST returns a fresh intent. */
|
|
12
|
+
| "IntentExpired"
|
|
13
|
+
/** An address in the intent encodes a different network from the Slip's. */
|
|
14
|
+
| "ForeignAddress"
|
|
15
|
+
/** A certificate or withdrawal needs the wallet's stake credential and the address carries none. */
|
|
16
|
+
| "NoStakeCredential"
|
|
17
|
+
/** The change address the wallet gave is not an address these bytes can be read as. */
|
|
18
|
+
| "UnreadableChangeAddress"
|
|
19
|
+
/** The intent withdraws rewards and nobody said what the reward balance is. */
|
|
20
|
+
| "RewardBalanceUnknown"
|
|
21
|
+
/** The wallet cannot cover the intent, the minimum ADA its outputs require, and the fee. */
|
|
22
|
+
| "InsufficientFunds"
|
|
23
|
+
/** The intent cannot be built into a valid transaction for any other reason. */
|
|
24
|
+
| "CannotBalance"
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Exhaustive by type: adding a refusal above without listing it here fails to
|
|
28
|
+
* compile, and `test/balance.test.ts` fails if any of them stops being reachable.
|
|
29
|
+
*/
|
|
30
|
+
export const balanceRefusals: Readonly<Record<BalanceRefusal, true>> = {
|
|
31
|
+
IntentExpired: true,
|
|
32
|
+
ForeignAddress: true,
|
|
33
|
+
NoStakeCredential: true,
|
|
34
|
+
UnreadableChangeAddress: true,
|
|
35
|
+
RewardBalanceUnknown: true,
|
|
36
|
+
InsufficientFunds: true,
|
|
37
|
+
CannotBalance: true
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A wrong-network address is `MALFORMED_RESPONSE` and not `WRONG_NETWORK`:
|
|
42
|
+
* the latter is a disagreement the person can act on by switching wallets, and
|
|
43
|
+
* an endpoint that sent an address on another chain is not one of those.
|
|
44
|
+
*/
|
|
45
|
+
export const slipErrorCodeFor = (refusal: BalanceRefusal): ClientErrorCode => {
|
|
46
|
+
switch (refusal) {
|
|
47
|
+
case "IntentExpired":
|
|
48
|
+
return "INTENT_EXPIRED"
|
|
49
|
+
case "ForeignAddress":
|
|
50
|
+
return "MALFORMED_RESPONSE"
|
|
51
|
+
case "InsufficientFunds":
|
|
52
|
+
return "INSUFFICIENT_FUNDS"
|
|
53
|
+
case "NoStakeCredential":
|
|
54
|
+
case "UnreadableChangeAddress":
|
|
55
|
+
case "RewardBalanceUnknown":
|
|
56
|
+
case "CannotBalance":
|
|
57
|
+
return "CANNOT_BALANCE"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** `cause` keeps what evolution-sdk said, so a build failure stays diagnosable. */
|
|
62
|
+
export class BalanceError extends Data.TaggedError("BalanceError")<{
|
|
63
|
+
readonly refusal: BalanceRefusal
|
|
64
|
+
readonly detail: string
|
|
65
|
+
readonly cause?: unknown
|
|
66
|
+
}> {
|
|
67
|
+
override get message(): string {
|
|
68
|
+
return this.detail
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
get code(): ClientErrorCode {
|
|
72
|
+
return slipErrorCodeFor(this.refusal)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const refuse = (refusal: BalanceRefusal, detail: string, cause?: unknown): BalanceError =>
|
|
77
|
+
new BalanceError({ refusal, detail, ...(cause === undefined ? {} : { cause }) })
|