1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
//! A wrapper around the asynchronous NBGL streaming review C API bindings
//! <ul>
//! <li>nbgl_useCaseReviewStreamingStart</li>
//! <li>nbgl_useCaseAdvancedReviewStreamingStart</li>
//! <li>nbgl_useCaseReviewStreamingBlindSigningStart</li>
//! <li>nbgl_useCaseReviewStreamingContinueExt</li>
//! <li>nbgl_useCaseReviewStreamingFinish</li>
//! </ul>
//!
//! Used to display streamed transaction review screens.
use super::*;
struct WarningDetailsType {
dapp_provider_name: CString,
report_url: CString,
report_provider: CString,
provider_message: CString,
}
/// A builder to create and show a streaming review flow.
pub struct NbglStreamingReview {
icon: nbgl_icon_details_t,
tx_type: TransactionType,
blind: bool,
skip: bool,
warning_details_type: Option<WarningDetailsType>,
}
impl SyncNBGL for NbglStreamingReview {}
/// Status returned by the `next` method.
pub enum NbglStreamingReviewStatus {
Next,
Rejected,
Skipped,
}
impl NbglStreamingReview {
/// Creates a new streaming review flow builder.
/// # Returns
/// Returns a new instance of `NbglStreamingReview`.
pub fn new() -> NbglStreamingReview {
NbglStreamingReview {
icon: nbgl_icon_details_t::default(),
tx_type: TransactionType::Transaction,
blind: false,
skip: false,
warning_details_type: None,
}
}
/// Sets the transaction type for the streaming review flow.
/// # Arguments
/// * `tx_type` - The transaction type to set.
/// # Returns
/// Returns the builder itself to allow method chaining.
pub fn tx_type(self, tx_type: TransactionType) -> NbglStreamingReview {
NbglStreamingReview { tx_type, ..self }
}
/// Enables blind signing mode for the streaming review flow.
/// # Returns
/// Returns the builder itself to allow method chaining.
pub fn blind(self) -> NbglStreamingReview {
NbglStreamingReview {
blind: true,
..self
}
}
/// Sets the icon to display in the center of the page.
/// # Arguments
/// * `glyph` - The icon to display in the center of the page.
/// # Returns
/// Returns the builder itself to allow method chaining.
pub fn glyph(self, glyph: &NbglGlyph) -> NbglStreamingReview {
NbglStreamingReview {
icon: glyph.into(),
..self
}
}
/// Makes the review skippable, adding a "Skip" button to the UI.
/// # Returns
/// Returns the builder itself to allow method chaining.
pub fn skippable(self) -> NbglStreamingReview {
NbglStreamingReview { skip: true, ..self }
}
/// Configures the warning details to display in case of a risky transaction.
/// # Arguments
/// * `dapp_provider` - The name of the dApp provider.
/// * `report_url` - The URL where the user can report the issue.
/// * `report_provider` - The name of the entity to which the issue can be reported.
/// * `provider_message` - A message from the provider regarding the warning.
/// # Returns
/// Returns the builder itself to allow method chaining.
pub fn warning_details(
self,
dapp_provider: Option<&str>,
report_url: Option<&str>,
report_provider: Option<&str>,
provider_message: Option<&str>,
) -> NbglStreamingReview {
NbglStreamingReview {
warning_details_type: Some(WarningDetailsType {
dapp_provider_name: match dapp_provider {
Some(s) => CString::new(s).unwrap(),
None => CString::default(),
},
report_url: match report_url {
Some(s) => CString::new(s).unwrap(),
None => CString::default(),
},
report_provider: match report_provider {
Some(s) => CString::new(s).unwrap(),
None => CString::default(),
},
provider_message: match provider_message {
Some(s) => CString::new(s).unwrap(),
None => CString::default(),
},
}),
..self
}
}
/// Starts the streaming review flow.
/// # Arguments
/// * `title` - The title to display at the top of the first page.
/// * `subtitle` - An optional subtitle to display below the title on the first page.
/// # Returns
/// Returns `true` if the user approved the transaction, `false` otherwise.
pub fn start(&self, title: &str, subtitle: Option<&str>) -> bool {
unsafe {
let title = CString::new(title).unwrap();
let subtitle = match subtitle {
Some(s) => CString::new(s).unwrap(),
None => CString::default(),
};
self.ux_sync_init();
match self.blind {
true => match &self.warning_details_type {
Some(w) => {
let warning_details = nbgl_warning_t {
predefinedSet: (1u32 << W3C_RISK_DETECTED_WARN),
dAppProvider: w.dapp_provider_name.as_ptr()
as *const ::core::ffi::c_char,
reportUrl: w.report_url.as_ptr() as *const ::core::ffi::c_char,
reportProvider: w.report_provider.as_ptr()
as *const ::core::ffi::c_char,
providerMessage: w.provider_message.as_ptr()
as *const ::core::ffi::c_char,
..Default::default()
};
nbgl_useCaseAdvancedReviewStreamingStart(
self.tx_type.to_c_type(self.skip),
&self.icon as *const nbgl_icon_details_t,
title.as_ptr() as *const c_char,
match subtitle.is_empty() {
true => core::ptr::null(),
false => subtitle.as_ptr() as *const c_char,
},
&warning_details as *const nbgl_warning_t,
Some(choice_callback),
);
}
None => {
nbgl_useCaseReviewStreamingBlindSigningStart(
self.tx_type.to_c_type(self.skip),
&self.icon as *const nbgl_icon_details_t,
title.as_ptr() as *const c_char,
match subtitle.is_empty() {
true => core::ptr::null(),
false => subtitle.as_ptr() as *const c_char,
},
Some(choice_callback),
);
}
},
false => {
nbgl_useCaseReviewStreamingStart(
self.tx_type.to_c_type(self.skip),
&self.icon as *const nbgl_icon_details_t,
title.as_ptr() as *const c_char,
match subtitle.is_empty() {
true => core::ptr::null(),
false => subtitle.as_ptr() as *const c_char,
},
Some(choice_callback),
);
}
}
let sync_ret = self.ux_sync_wait(false);
// Return true if the user approved the transaction, false otherwise.
match sync_ret {
SyncNbgl::UxSyncRetApproved => {
return true;
}
_ => {
return false;
}
}
}
}
#[deprecated(note = "use next instead")]
pub fn continue_review(&self, fields: &[Field]) -> bool {
unsafe {
let v: Vec<CField> = fields
.iter()
.map(|f| CField {
name: CString::new(f.name).unwrap(),
value: CString::new(f.value).unwrap(),
})
.collect();
// Fill the tag_value_array with the fields converted to nbgl_contentTagValue_t
let mut tag_value_array: Vec<nbgl_contentTagValue_t> = Vec::new();
for field in v.iter() {
let val = nbgl_contentTagValue_t {
item: field.name.as_ptr() as *const ::core::ffi::c_char,
value: field.value.as_ptr() as *const ::core::ffi::c_char,
..Default::default()
};
tag_value_array.push(val);
}
// Create the tag_value_list with the tag_value_array.
let tag_value_list = nbgl_contentTagValueList_t {
pairs: tag_value_array.as_ptr() as *const nbgl_contentTagValue_t,
nbPairs: fields.len() as u8,
..Default::default()
};
self.ux_sync_init();
nbgl_useCaseReviewStreamingContinue(
&tag_value_list as *const nbgl_contentTagValueList_t,
Some(choice_callback),
);
let sync_ret = self.ux_sync_wait(false);
// Return true if the user approved the transaction, false otherwise.
match sync_ret {
SyncNbgl::UxSyncRetApproved => {
return true;
}
_ => {
return false;
}
}
}
}
/// Proceeds to the next page in the streaming review flow with the provided fields.
/// # Arguments
/// * `fields` - A slice of `Field` representing the tag/value pairs to display on the next page.
/// # Returns
/// Returns an `NbglStreamingReviewStatus` indicating whether the user proceeded to the next
/// page, skipped the review, or rejected it.
pub fn next(&self, fields: &[Field]) -> NbglStreamingReviewStatus {
unsafe {
let v: Vec<CField> = fields
.iter()
.map(|f| CField {
name: CString::new(f.name).unwrap(),
value: CString::new(f.value).unwrap(),
})
.collect();
// Fill the tag_value_array with the fields converted to nbgl_contentTagValue_t
let mut tag_value_array: Vec<nbgl_contentTagValue_t> = Vec::new();
for field in v.iter() {
let val = nbgl_contentTagValue_t {
item: field.name.as_ptr() as *const ::core::ffi::c_char,
value: field.value.as_ptr() as *const ::core::ffi::c_char,
..Default::default()
};
tag_value_array.push(val);
}
// Create the tag_value_list with the tag_value_array.
let tag_value_list = nbgl_contentTagValueList_t {
pairs: tag_value_array.as_ptr() as *const nbgl_contentTagValue_t,
nbPairs: fields.len() as u8,
..Default::default()
};
self.ux_sync_init();
nbgl_useCaseReviewStreamingContinueExt(
&tag_value_list as *const nbgl_contentTagValueList_t,
Some(choice_callback),
Some(skip_callback),
);
let sync_ret = self.ux_sync_wait(false);
// Return true if the user approved the transaction, false otherwise.
match sync_ret {
SyncNbgl::UxSyncRetApproved => {
return NbglStreamingReviewStatus::Next;
}
SyncNbgl::UxSyncRetSkipped => {
return NbglStreamingReviewStatus::Skipped;
}
_ => {
return NbglStreamingReviewStatus::Rejected;
}
}
}
}
/// Finishes the streaming review flow by displaying the final confirmation page.
/// # Arguments
/// * `finish_title` - The title to display on the final confirmation page.
/// # Returns
/// Returns `true` if the user approved the transaction, `false` otherwise.
pub fn finish(&self, finish_title: &str) -> bool {
unsafe {
let finish_title = CString::new(finish_title).unwrap();
self.ux_sync_init();
nbgl_useCaseReviewStreamingFinish(
finish_title.as_ptr() as *const c_char,
Some(choice_callback),
);
let sync_ret = self.ux_sync_wait(false);
// Return true if the user approved the transaction, false otherwise.
match sync_ret {
SyncNbgl::UxSyncRetApproved => {
return true;
}
_ => {
return false;
}
}
}
}
}