@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.
- package/README.md +53 -9
- package/dist/address.d.ts +12 -0
- package/dist/address.d.ts.map +1 -1
- package/dist/address.js +43 -0
- package/dist/address.js.map +1 -1
- 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/card.css +492 -0
- package/dist/card.d.ts +41 -0
- package/dist/card.d.ts.map +1 -0
- package/dist/card.js +69 -0
- package/dist/card.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 +57 -0
- package/dist/complete-error.d.ts.map +1 -0
- package/dist/complete-error.js +52 -0
- package/dist/complete-error.js.map +1 -0
- package/dist/complete.d.ts +77 -0
- package/dist/complete.d.ts.map +1 -0
- package/dist/complete.js +182 -0
- package/dist/complete.js.map +1 -0
- package/dist/derived.d.ts +8 -0
- package/dist/derived.d.ts.map +1 -0
- package/dist/derived.js +2 -0
- package/dist/derived.js.map +1 -0
- package/dist/effects-panel.d.ts +37 -0
- package/dist/effects-panel.d.ts.map +1 -0
- package/dist/effects-panel.js +64 -0
- package/dist/effects-panel.js.map +1 -0
- package/dist/effects.css +629 -0
- package/dist/exchange.d.ts +54 -0
- package/dist/exchange.d.ts.map +1 -0
- package/dist/exchange.js +136 -0
- package/dist/exchange.js.map +1 -0
- package/dist/explain.d.ts +39 -0
- package/dist/explain.d.ts.map +1 -0
- package/dist/explain.js +172 -0
- package/dist/explain.js.map +1 -0
- package/dist/index.d.ts +34 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -2
- package/dist/index.js.map +1 -1
- package/dist/ledger.d.ts +74 -0
- package/dist/ledger.d.ts.map +1 -0
- package/dist/ledger.js +402 -0
- package/dist/ledger.js.map +1 -0
- package/dist/outcome-panel.d.ts +63 -0
- package/dist/outcome-panel.d.ts.map +1 -0
- package/dist/outcome-panel.js +40 -0
- package/dist/outcome-panel.js.map +1 -0
- package/dist/parameter-form.d.ts +26 -0
- package/dist/parameter-form.d.ts.map +1 -0
- package/dist/parameter-form.js +42 -0
- package/dist/parameter-form.js.map +1 -0
- 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/slip-card.d.ts +55 -0
- package/dist/slip-card.d.ts.map +1 -0
- package/dist/slip-card.js +89 -0
- package/dist/slip-card.js.map +1 -0
- package/dist/tokens.css +97 -123
- 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 +13 -6
- package/src/address.ts +53 -0
- package/src/balance-error.ts +77 -0
- package/src/balance.ts +285 -0
- package/src/card.css +492 -0
- package/src/card.ts +86 -0
- package/src/cip30.ts +19 -0
- package/src/complete-error.ts +95 -0
- package/src/complete.ts +307 -0
- package/src/derived.ts +8 -0
- package/src/effects-panel.tsx +386 -0
- package/src/effects.css +629 -0
- package/src/exchange.ts +212 -0
- package/src/explain.ts +211 -0
- package/src/index.ts +64 -3
- package/src/ledger.ts +503 -0
- package/src/outcome-panel.tsx +263 -0
- package/src/parameter-form.tsx +138 -0
- package/src/resolve.ts +40 -0
- package/src/sign-error.ts +89 -0
- package/src/sign.ts +118 -0
- package/src/slip-card.tsx +244 -0
- package/src/tokens.css +97 -123
- package/src/utxo.ts +70 -0
- package/src/witness.ts +132 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The transaction preview, drawn from `4 · Anatomy` and `6 · Preview states` on
|
|
5
|
+
* the design sheet. Every figure is derived from the transaction; the claim is
|
|
6
|
+
* only the title it is held against. A mismatch removes the sign button rather
|
|
7
|
+
* than disabling it, and no prop brings it back.
|
|
8
|
+
*/
|
|
9
|
+
import type { Verdict } from "@cardano-slips/verifier"
|
|
10
|
+
import { useEffect, useId, useState } from "react"
|
|
11
|
+
|
|
12
|
+
import type { Derived } from "./derived.js"
|
|
13
|
+
import { formatCountdown, secondsLeft } from "./explain.js"
|
|
14
|
+
import {
|
|
15
|
+
blockedLine,
|
|
16
|
+
evidenceOf,
|
|
17
|
+
groupsOf,
|
|
18
|
+
headlineOf,
|
|
19
|
+
isCollapsed,
|
|
20
|
+
isGrouped,
|
|
21
|
+
ledgerOf,
|
|
22
|
+
matchLine,
|
|
23
|
+
rawLines,
|
|
24
|
+
type Group,
|
|
25
|
+
type Row
|
|
26
|
+
} from "./ledger.js"
|
|
27
|
+
|
|
28
|
+
export type EffectsPanelProps = {
|
|
29
|
+
/** What the card promised, restated so it can be compared with what follows. */
|
|
30
|
+
readonly claim: string
|
|
31
|
+
/** The card's description, set beside the transaction when the two disagree. */
|
|
32
|
+
readonly description?: string
|
|
33
|
+
/** The host that served the link, as a host only. Whether it is verified is the identity chip's to say. */
|
|
34
|
+
readonly origin?: string
|
|
35
|
+
readonly derived: Derived
|
|
36
|
+
/** The verifier's own verdict. Required, so no caller renders a match by leaving the reasons out. */
|
|
37
|
+
readonly verdict: Verdict
|
|
38
|
+
/** Read once a second for the countdown. */
|
|
39
|
+
readonly clock?: () => number
|
|
40
|
+
readonly onSign?: () => void
|
|
41
|
+
readonly onCancel?: () => void
|
|
42
|
+
readonly onRebuild?: () => void
|
|
43
|
+
/** "Report this link" is offered only where there is somewhere to send the report. */
|
|
44
|
+
readonly onReport?: () => void
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Under this, the countdown leaves the meta row and takes the headline's place (state f). */
|
|
48
|
+
const expiringUnder = 60
|
|
49
|
+
|
|
50
|
+
const groupNames: { readonly [group in Group]: string } = {
|
|
51
|
+
arriving: "Arriving",
|
|
52
|
+
leaving: "Leaving",
|
|
53
|
+
unmoved: "No value moved"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const markWords = { "not-in-the-link": "Not in the link", "not-as-the-link-says": "Not as the link says" } as const
|
|
57
|
+
|
|
58
|
+
const chipWords = { refundable: "Refundable", "as-stated": "As stated" } as const
|
|
59
|
+
|
|
60
|
+
const useNow = (clock: () => number): number => {
|
|
61
|
+
const [now, setNow] = useState(clock)
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
const timer = setInterval(() => setNow(clock()), 1000)
|
|
64
|
+
return () => clearInterval(timer)
|
|
65
|
+
}, [clock])
|
|
66
|
+
return now
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const LedgerRow = ({ row }: { readonly row: Row }): React.JSX.Element => (
|
|
70
|
+
<div className="slip-panel__row" data-mark={row.mark}>
|
|
71
|
+
<div className="slip-panel__row-name">
|
|
72
|
+
<div className="slip-panel__row-label">
|
|
73
|
+
<span className="slip-panel__label">{row.label}</span>
|
|
74
|
+
{row.chip === undefined ? undefined : (
|
|
75
|
+
<span className="slip-panel__chip" data-chip={row.chip}>
|
|
76
|
+
{chipWords[row.chip]}
|
|
77
|
+
</span>
|
|
78
|
+
)}
|
|
79
|
+
{row.mark === undefined ? undefined : (
|
|
80
|
+
<span className="slip-panel__chip" data-chip="mark">
|
|
81
|
+
{markWords[row.mark]}
|
|
82
|
+
</span>
|
|
83
|
+
)}
|
|
84
|
+
</div>
|
|
85
|
+
{row.detail === undefined ? undefined : <span className="slip-panel__detail">{row.detail}</span>}
|
|
86
|
+
</div>
|
|
87
|
+
<div className="slip-panel__amounts">
|
|
88
|
+
{row.identifier === undefined ? undefined : <span className="slip-panel__identifier">{row.identifier}</span>}
|
|
89
|
+
{row.amounts.map((amount, index) => (
|
|
90
|
+
<span key={index} className="slip-panel__amount" data-tone={amount.tone}>
|
|
91
|
+
{amount.text}
|
|
92
|
+
</span>
|
|
93
|
+
))}
|
|
94
|
+
</div>
|
|
95
|
+
{row.note === undefined ? undefined : <p className="slip-panel__note">{row.note}</p>}
|
|
96
|
+
</div>
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
const Rows = ({ rows }: { readonly rows: ReadonlyArray<Row> }): React.JSX.Element => (
|
|
100
|
+
<div className="slip-panel__ledger">
|
|
101
|
+
{rows.map((row) => (
|
|
102
|
+
<LedgerRow key={row.key} row={row} />
|
|
103
|
+
))}
|
|
104
|
+
</div>
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Past eight rows each group folds to its header and a count. A group holding
|
|
109
|
+
* a marked row never starts folded: the one thing the block points at is not
|
|
110
|
+
* something a person should have to open.
|
|
111
|
+
*/
|
|
112
|
+
export const Ledger = ({ rows }: { readonly rows: ReadonlyArray<Row> }): React.JSX.Element => {
|
|
113
|
+
if (!isGrouped(rows)) return <Rows rows={rows} />
|
|
114
|
+
const collapsed = isCollapsed(rows)
|
|
115
|
+
return (
|
|
116
|
+
<div className="slip-panel__groups">
|
|
117
|
+
{groupsOf(rows).map(({ group, rows: inGroup }) =>
|
|
118
|
+
collapsed ? (
|
|
119
|
+
<details
|
|
120
|
+
key={group}
|
|
121
|
+
className="slip-panel__group"
|
|
122
|
+
open={inGroup.some((row) => row.mark !== undefined) || undefined}
|
|
123
|
+
>
|
|
124
|
+
<summary className="slip-panel__group-name">
|
|
125
|
+
{groupNames[group]} · {inGroup.length}
|
|
126
|
+
</summary>
|
|
127
|
+
<Rows rows={inGroup} />
|
|
128
|
+
</details>
|
|
129
|
+
) : (
|
|
130
|
+
<div key={group} className="slip-panel__group">
|
|
131
|
+
<p className="slip-panel__group-name">{groupNames[group]}</p>
|
|
132
|
+
<Rows rows={inGroup} />
|
|
133
|
+
</div>
|
|
134
|
+
)
|
|
135
|
+
)}
|
|
136
|
+
</div>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const Raw = ({ derived }: { readonly derived: Derived }): React.JSX.Element => (
|
|
141
|
+
<pre className="slip-panel__raw-lines">{rawLines(derived).join("\n")}</pre>
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
const Countdown = ({ seconds }: { readonly seconds: number }): React.JSX.Element => (
|
|
145
|
+
// The server and the browser read the clock a moment apart.
|
|
146
|
+
<span suppressHydrationWarning>{formatCountdown(seconds)}</span>
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
const Blocked = ({
|
|
150
|
+
claim,
|
|
151
|
+
description,
|
|
152
|
+
derived,
|
|
153
|
+
reasons,
|
|
154
|
+
onCancel,
|
|
155
|
+
onReport
|
|
156
|
+
}: Pick<EffectsPanelProps, "claim" | "description" | "derived" | "onCancel" | "onReport"> & {
|
|
157
|
+
readonly reasons: Extract<Verdict, { _tag: "mismatch" }>["reasons"]
|
|
158
|
+
}): React.JSX.Element => {
|
|
159
|
+
const [rawOpen, setRawOpen] = useState(false)
|
|
160
|
+
const title = useId()
|
|
161
|
+
const rows = ledgerOf(derived, reasons)
|
|
162
|
+
return (
|
|
163
|
+
<section className="slip-root slip-panel" data-blocked="" aria-labelledby={title}>
|
|
164
|
+
<div className="slip-panel__claim" role="alert">
|
|
165
|
+
<p className="slip-panel__kicker" data-tone="bad">
|
|
166
|
+
Signing blocked
|
|
167
|
+
</p>
|
|
168
|
+
<h2 className="slip-panel__title" id={title}>
|
|
169
|
+
This transaction doesn't do what the link says
|
|
170
|
+
</h2>
|
|
171
|
+
<p className="slip-panel__lead">{blockedLine(reasons)}</p>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
<div className="slip-panel__compare">
|
|
175
|
+
<div className="slip-panel__side">
|
|
176
|
+
<p className="slip-panel__kicker">The link claims</p>
|
|
177
|
+
<p className="slip-panel__claimed">{claim}</p>
|
|
178
|
+
{description === undefined ? undefined : <p className="slip-panel__note">{description}</p>}
|
|
179
|
+
</div>
|
|
180
|
+
<div className="slip-panel__side" data-tone="bad">
|
|
181
|
+
<p className="slip-panel__kicker" data-tone="bad">
|
|
182
|
+
The transaction does
|
|
183
|
+
</p>
|
|
184
|
+
{reasons.map((reason, index) => {
|
|
185
|
+
const { figure, lines } = evidenceOf(reason, derived)
|
|
186
|
+
return (
|
|
187
|
+
<div key={index} className="slip-panel__evidence">
|
|
188
|
+
{figure === undefined ? undefined : <p className="slip-panel__evidence-figure">{figure}</p>}
|
|
189
|
+
{lines.map((line) => (
|
|
190
|
+
<p key={line} className="slip-panel__detail">
|
|
191
|
+
{line}
|
|
192
|
+
</p>
|
|
193
|
+
))}
|
|
194
|
+
</div>
|
|
195
|
+
)
|
|
196
|
+
})}
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<Ledger rows={rows} />
|
|
201
|
+
|
|
202
|
+
{rawOpen ? <Raw derived={derived} /> : undefined}
|
|
203
|
+
|
|
204
|
+
<div className="slip-panel__exits">
|
|
205
|
+
<button type="button" className="slip-panel__button" data-kind="close" onClick={onCancel}>
|
|
206
|
+
Close without signing
|
|
207
|
+
</button>
|
|
208
|
+
<div className="slip-panel__quiet">
|
|
209
|
+
<button
|
|
210
|
+
type="button"
|
|
211
|
+
className="slip-panel__button"
|
|
212
|
+
data-kind="quiet"
|
|
213
|
+
aria-expanded={rawOpen}
|
|
214
|
+
onClick={() => setRawOpen((open) => !open)}
|
|
215
|
+
>
|
|
216
|
+
{rawOpen ? "Hide raw transaction" : "Show raw transaction"}
|
|
217
|
+
</button>
|
|
218
|
+
{onReport === undefined ? undefined : (
|
|
219
|
+
<button type="button" className="slip-panel__button" data-kind="quiet" onClick={onReport}>
|
|
220
|
+
Report this link
|
|
221
|
+
</button>
|
|
222
|
+
)}
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
</section>
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export const EffectsPanel = ({
|
|
230
|
+
claim,
|
|
231
|
+
description,
|
|
232
|
+
origin,
|
|
233
|
+
derived,
|
|
234
|
+
verdict,
|
|
235
|
+
clock = Date.now,
|
|
236
|
+
onSign,
|
|
237
|
+
onCancel,
|
|
238
|
+
onRebuild,
|
|
239
|
+
onReport
|
|
240
|
+
}: EffectsPanelProps): React.JSX.Element => {
|
|
241
|
+
const now = useNow(clock)
|
|
242
|
+
const [rawOpen, setRawOpen] = useState(false)
|
|
243
|
+
const title = useId()
|
|
244
|
+
|
|
245
|
+
if (verdict._tag === "mismatch") {
|
|
246
|
+
return (
|
|
247
|
+
<Blocked
|
|
248
|
+
claim={claim}
|
|
249
|
+
derived={derived}
|
|
250
|
+
reasons={verdict.reasons}
|
|
251
|
+
{...(description === undefined ? {} : { description })}
|
|
252
|
+
{...(onCancel === undefined ? {} : { onCancel })}
|
|
253
|
+
{...(onReport === undefined ? {} : { onReport })}
|
|
254
|
+
/>
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const rows = ledgerOf(derived)
|
|
259
|
+
const until = derived.effects.validity.validUntil
|
|
260
|
+
const seconds = until === null ? undefined : secondsLeft(until.time, BigInt(now))
|
|
261
|
+
|
|
262
|
+
if (seconds === 0) {
|
|
263
|
+
return (
|
|
264
|
+
<section className="slip-root slip-panel" data-expired="" aria-labelledby={title}>
|
|
265
|
+
<div className="slip-panel__claim">
|
|
266
|
+
<p className="slip-panel__kicker" data-tone="warn">
|
|
267
|
+
Expired
|
|
268
|
+
</p>
|
|
269
|
+
<h2 className="slip-panel__title" id={title}>
|
|
270
|
+
This transaction ran out of time
|
|
271
|
+
</h2>
|
|
272
|
+
<p className="slip-panel__lead">
|
|
273
|
+
Nothing was signed and nothing left your wallet. Building a new one takes a second, and the amounts will be
|
|
274
|
+
checked again.
|
|
275
|
+
</p>
|
|
276
|
+
</div>
|
|
277
|
+
<div className="slip-panel__stale">
|
|
278
|
+
<Ledger rows={rows} />
|
|
279
|
+
</div>
|
|
280
|
+
<div className="slip-panel__buttons">
|
|
281
|
+
<button type="button" className="slip-panel__button" data-kind="cancel" onClick={onCancel}>
|
|
282
|
+
Cancel
|
|
283
|
+
</button>
|
|
284
|
+
<button type="button" className="slip-panel__button" data-kind="primary" onClick={onRebuild}>
|
|
285
|
+
Build a new transaction
|
|
286
|
+
</button>
|
|
287
|
+
</div>
|
|
288
|
+
</section>
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const headline = headlineOf(derived)
|
|
293
|
+
const expiring = seconds !== undefined && seconds < expiringUnder
|
|
294
|
+
|
|
295
|
+
return (
|
|
296
|
+
<section className="slip-root slip-panel" aria-labelledby={title}>
|
|
297
|
+
<div className="slip-panel__claim">
|
|
298
|
+
<p className="slip-panel__kicker">What you're signing</p>
|
|
299
|
+
<h2 className="slip-panel__title" id={title}>
|
|
300
|
+
{claim}
|
|
301
|
+
</h2>
|
|
302
|
+
{origin === undefined ? undefined : <span className="slip-panel__origin">{origin}</span>}
|
|
303
|
+
</div>
|
|
304
|
+
|
|
305
|
+
{expiring ? (
|
|
306
|
+
<div className="slip-panel__expiring" role="timer">
|
|
307
|
+
<div className="slip-panel__expiring-head">
|
|
308
|
+
<span>This transaction expires in</span>
|
|
309
|
+
<span className="slip-panel__expiring-clock">
|
|
310
|
+
<Countdown seconds={seconds} />
|
|
311
|
+
</span>
|
|
312
|
+
</div>
|
|
313
|
+
<div className="slip-panel__bar" aria-hidden="true">
|
|
314
|
+
<span
|
|
315
|
+
className="slip-panel__bar-fill"
|
|
316
|
+
suppressHydrationWarning
|
|
317
|
+
style={{ width: `${(seconds / expiringUnder) * 100}%` }}
|
|
318
|
+
/>
|
|
319
|
+
</div>
|
|
320
|
+
<p className="slip-panel__note">After that the amounts could be stale, so we'll build a fresh one.</p>
|
|
321
|
+
</div>
|
|
322
|
+
) : (
|
|
323
|
+
<div className="slip-panel__headline">
|
|
324
|
+
<div className="slip-panel__figure">
|
|
325
|
+
<span className="slip-panel__note">{headline.label}</span>
|
|
326
|
+
<span className="slip-panel__figure-amount" data-tone={headline.tone}>
|
|
327
|
+
{headline.amount}
|
|
328
|
+
</span>
|
|
329
|
+
{headline.fee === undefined ? undefined : <span className="slip-panel__note">{headline.fee}</span>}
|
|
330
|
+
</div>
|
|
331
|
+
{headline.held === undefined ? undefined : (
|
|
332
|
+
<>
|
|
333
|
+
<span className="slip-panel__rule" aria-hidden="true" />
|
|
334
|
+
<div className="slip-panel__figure">
|
|
335
|
+
<span className="slip-panel__note">{headline.held.label}</span>
|
|
336
|
+
<span className="slip-panel__figure-amount" data-tone={headline.held.tone}>
|
|
337
|
+
{headline.held.amount}
|
|
338
|
+
</span>
|
|
339
|
+
</div>
|
|
340
|
+
</>
|
|
341
|
+
)}
|
|
342
|
+
</div>
|
|
343
|
+
)}
|
|
344
|
+
|
|
345
|
+
<Ledger rows={rows} />
|
|
346
|
+
|
|
347
|
+
{expiring || seconds === undefined ? undefined : (
|
|
348
|
+
<div className="slip-panel__expiry">
|
|
349
|
+
<span className="slip-panel__note">Expires in</span>
|
|
350
|
+
<span className="slip-panel__clock">
|
|
351
|
+
<Countdown seconds={seconds} />
|
|
352
|
+
</span>
|
|
353
|
+
</div>
|
|
354
|
+
)}
|
|
355
|
+
|
|
356
|
+
<p className="slip-panel__verdict">
|
|
357
|
+
<span className="slip-panel__tick" aria-hidden="true">
|
|
358
|
+
✓
|
|
359
|
+
</span>
|
|
360
|
+
<span>{matchLine(derived, rows)}</span>
|
|
361
|
+
</p>
|
|
362
|
+
|
|
363
|
+
<div className="slip-panel__buttons">
|
|
364
|
+
<button type="button" className="slip-panel__button" data-kind="cancel" onClick={onCancel}>
|
|
365
|
+
Cancel
|
|
366
|
+
</button>
|
|
367
|
+
<button type="button" className="slip-panel__button" data-kind="primary" onClick={onSign}>
|
|
368
|
+
Sign transaction
|
|
369
|
+
</button>
|
|
370
|
+
</div>
|
|
371
|
+
|
|
372
|
+
<div className="slip-panel__raw">
|
|
373
|
+
<button
|
|
374
|
+
type="button"
|
|
375
|
+
className="slip-panel__raw-toggle"
|
|
376
|
+
aria-expanded={rawOpen}
|
|
377
|
+
onClick={() => setRawOpen((open) => !open)}
|
|
378
|
+
>
|
|
379
|
+
<span aria-hidden="true">{rawOpen ? "▾" : "▸"}</span>
|
|
380
|
+
<span>Raw transaction</span>
|
|
381
|
+
</button>
|
|
382
|
+
{rawOpen ? <Raw derived={derived} /> : undefined}
|
|
383
|
+
</div>
|
|
384
|
+
</section>
|
|
385
|
+
)
|
|
386
|
+
}
|