@@ -20,20 +20,27 @@ use ledger_device_sdk::nbgl::{Field, TagValueList};
20
20
use ledger_device_sdk:: ui:: bitmaps:: { CHECKMARK , CROSS , EYE , WARNING } ;
21
21
use utils:: {
22
22
base58:: ALPHABET ,
23
- types:: { AssetOutput , Byte32 , LockupScript , TxInput , UnlockScript , UnsignedTx , I32 , U256 } ,
23
+ types:: {
24
+ AssetOutput , Byte32 , Hash , LockupScript , Token , TxInput , UnlockScript , UnsignedTx , I32 ,
25
+ U256 ,
26
+ } ,
24
27
} ;
25
28
26
29
#[ link_section = ".nvm_data" ]
27
30
static mut DATA : NVMData < NVM < NVM_DATA_SIZE > > = NVMData :: new ( NVM :: zeroed ( ) ) ;
28
31
29
32
const FIRST_OUTPUT_INDEX : u16 = 1 ;
33
+ pub const TOKEN_METADATA_SIZE : usize = 46 ;
34
+ const MAX_TOKEN_SYMBOL_LENGTH : usize = 12 ;
35
+ type TokenSymbol = [ u8 ; MAX_TOKEN_SYMBOL_LENGTH ] ;
30
36
31
37
pub struct TxReviewer {
32
38
buffer : SwappingBuffer < ' static , RAM_SIZE , NVM_DATA_SIZE > ,
33
39
has_external_inputs : bool ,
34
40
next_output_index : u16 ,
35
41
tx_fee : Option < U256 > ,
36
42
is_tx_execute_script : bool ,
43
+ token_metadata_length : usize ,
37
44
}
38
45
39
46
impl TxReviewer {
@@ -44,21 +51,29 @@ impl TxReviewer {
44
51
next_output_index : FIRST_OUTPUT_INDEX , // display output from index 1, similar to BTC
45
52
tx_fee : None ,
46
53
is_tx_execute_script : false ,
54
+ token_metadata_length : 0 ,
47
55
}
48
56
}
49
57
50
58
#[ inline]
51
- fn reset_buffer ( & mut self ) {
52
- self . buffer . reset ( ) ;
59
+ fn reset_buffer ( & mut self , from_index : usize ) {
60
+ self . buffer . reset ( from_index ) ;
53
61
}
54
62
55
63
#[ inline]
56
- pub fn init ( & mut self , is_tx_execute_script : bool ) {
57
- self . reset_buffer ( ) ;
64
+ pub fn init (
65
+ & mut self ,
66
+ is_tx_execute_script : bool ,
67
+ token_metadata : & [ u8 ] ,
68
+ ) -> Result < ( ) , ErrorCode > {
69
+ self . reset_buffer ( 0 ) ;
70
+ self . buffer . write ( token_metadata) ?;
58
71
self . has_external_inputs = false ;
59
72
self . next_output_index = FIRST_OUTPUT_INDEX ;
60
73
self . tx_fee = None ;
61
74
self . is_tx_execute_script = is_tx_execute_script;
75
+ self . token_metadata_length = token_metadata. len ( ) ;
76
+ Ok ( ( ) )
62
77
}
63
78
64
79
fn write_alph_amount ( & mut self , u256 : & U256 ) -> Result < usize , ErrorCode > {
@@ -67,12 +82,31 @@ impl TxReviewer {
67
82
self . buffer . write ( amount_str)
68
83
}
69
84
70
- fn write_token_amount ( & mut self , u256 : & U256 ) -> Result < usize , ErrorCode > {
85
+ fn write_token_raw_amount ( & mut self , u256 : & U256 ) -> Result < usize , ErrorCode > {
71
86
let mut amount_output = [ 0u8 ; 78 ] ; // u256 max
72
87
let amount_str = u256. to_str ( & mut amount_output) . unwrap ( ) ;
73
88
self . buffer . write ( amount_str)
74
89
}
75
90
91
+ fn write_token_amount (
92
+ & mut self ,
93
+ u256 : & U256 ,
94
+ symbol : TokenSymbol ,
95
+ decimals : usize ,
96
+ ) -> Result < usize , ErrorCode > {
97
+ let mut amount_output = [ 0u8 ; 86 ] ; // u256 max
98
+ let symbol_bytes = get_token_symbol_bytes ( & symbol[ ..] ) ;
99
+ amount_output[ ..symbol_bytes. len ( ) ] . copy_from_slice ( symbol_bytes) ;
100
+ amount_output[ symbol_bytes. len ( ) ] = b' ' ;
101
+ let prefix_length = symbol_bytes. len ( ) + 1 ;
102
+ let amount_str = u256. to_str_with_decimals ( & mut amount_output[ prefix_length..] , decimals) ;
103
+ if amount_str. is_none ( ) {
104
+ return Err ( ErrorCode :: Overflow ) ;
105
+ }
106
+ let total_length = prefix_length + amount_str. unwrap ( ) . len ( ) ;
107
+ self . buffer . write ( & amount_output[ ..total_length] )
108
+ }
109
+
76
110
fn write_token_id ( & mut self , token_id : & Byte32 ) -> Result < usize , ErrorCode > {
77
111
let hex_str: [ u8 ; 64 ] = utils:: to_hex ( & token_id. 0 ) . unwrap ( ) ;
78
112
self . buffer . write ( & hex_str)
@@ -188,6 +222,25 @@ impl TxReviewer {
188
222
self . buffer . write ( str_bytes)
189
223
}
190
224
225
+ fn get_token_metadata ( & self , token_id : & Hash ) -> Option < ( TokenSymbol , u8 ) > {
226
+ let token_size = self . token_metadata_length / TOKEN_METADATA_SIZE ;
227
+ if token_size == 0 {
228
+ return None ;
229
+ }
230
+ for i in 0 ..token_size {
231
+ let from_index = i * TOKEN_METADATA_SIZE ;
232
+ let to_index = from_index + TOKEN_METADATA_SIZE ;
233
+ let token_metadata_bytes = self . buffer . read ( from_index, to_index) ;
234
+ if token_metadata_bytes[ 1 ..33 ] == token_id. 0 {
235
+ let last_index = TOKEN_METADATA_SIZE - 1 ;
236
+ let token_symbol = token_metadata_bytes[ 33 ..last_index] . try_into ( ) . unwrap ( ) ;
237
+ let token_decimals = token_metadata_bytes[ last_index] ;
238
+ return Some ( ( token_symbol, token_decimals) ) ;
239
+ }
240
+ }
241
+ None
242
+ }
243
+
191
244
fn prepare_output (
192
245
& mut self ,
193
246
output : & AssetOutput ,
@@ -228,21 +281,39 @@ impl TxReviewer {
228
281
229
282
// Asset output has at most one token
230
283
let token = output. tokens . get_current_item ( ) . unwrap ( ) ;
231
- let token_id_from_index = self . buffer . get_index ( ) ;
232
- let token_id_to_index = self . write_token_id ( & token. id ) ?;
233
-
234
- let token_amount_from_index = self . buffer . get_index ( ) ;
235
- let token_amount_to_index = self . write_token_amount ( & token. amount ) ?;
236
-
284
+ let token_indexes = self . prepare_token ( token) ?;
237
285
Ok ( Some ( OutputIndexes {
238
- token : Some ( TokenIndexes {
239
- token_id : ( token_id_from_index, token_id_to_index) ,
240
- token_amount : ( token_amount_from_index, token_amount_to_index) ,
241
- } ) ,
286
+ token : Some ( token_indexes) ,
242
287
..output_indexes
243
288
} ) )
244
289
}
245
290
291
+ fn prepare_token ( & mut self , token : & Token ) -> Result < TokenIndexes , ErrorCode > {
292
+ let token_id_from_index = self . buffer . get_index ( ) ;
293
+ let token_id_to_index = self . write_token_id ( & token. id ) ?;
294
+ match self . get_token_metadata ( & token. id ) {
295
+ Some ( ( token_symbol, token_decimals) ) => {
296
+ let token_amount_from_index = self . buffer . get_index ( ) ;
297
+ let token_amount_to_index =
298
+ self . write_token_amount ( & token. amount , token_symbol, token_decimals as usize ) ?;
299
+ Ok ( TokenIndexes {
300
+ has_token_metadata : true ,
301
+ token_id : ( token_id_from_index, token_id_to_index) ,
302
+ token_amount : ( token_amount_from_index, token_amount_to_index) ,
303
+ } )
304
+ }
305
+ None => {
306
+ let token_amount_from_index = self . buffer . get_index ( ) ;
307
+ let token_amount_to_index = self . write_token_raw_amount ( & token. amount ) ?;
308
+ Ok ( TokenIndexes {
309
+ has_token_metadata : false ,
310
+ token_id : ( token_id_from_index, token_id_to_index) ,
311
+ token_amount : ( token_amount_from_index, token_amount_to_index) ,
312
+ } )
313
+ }
314
+ }
315
+ }
316
+
246
317
fn get_str_from_range ( & self , range : ( usize , usize ) ) -> Result < & str , ErrorCode > {
247
318
let bytes = self . buffer . read ( range. 0 , range. 1 ) ;
248
319
bytes_to_string ( bytes)
@@ -310,23 +381,39 @@ impl TxReviewer {
310
381
}
311
382
312
383
let TokenIndexes {
384
+ has_token_metadata,
313
385
token_id,
314
386
token_amount,
315
387
} = token. unwrap ( ) ;
316
388
let token_id = self . get_str_from_range ( token_id) ?;
317
389
let token_amount = self . get_str_from_range ( token_amount) ?;
318
- let fields = [
319
- alph_amount_field,
320
- Field {
321
- name : "Token ID" ,
322
- value : token_id,
323
- } ,
324
- Field {
325
- name : "Raw Amount" ,
326
- value : token_amount,
327
- } ,
328
- address_field,
329
- ] ;
390
+ let fields = if has_token_metadata {
391
+ [
392
+ Field {
393
+ name : "Token ID" ,
394
+ value : token_id,
395
+ } ,
396
+ Field {
397
+ name : "Token Amount" ,
398
+ value : token_amount,
399
+ } ,
400
+ alph_amount_field,
401
+ address_field,
402
+ ]
403
+ } else {
404
+ [
405
+ Field {
406
+ name : "Token ID" ,
407
+ value : token_id,
408
+ } ,
409
+ Field {
410
+ name : "Raw Amount" ,
411
+ value : token_amount,
412
+ } ,
413
+ alph_amount_field,
414
+ address_field,
415
+ ]
416
+ } ;
330
417
review ( & fields, review_message)
331
418
}
332
419
@@ -362,7 +449,7 @@ impl TxReviewer {
362
449
if let Some ( current_output) = outputs. get_current_item ( ) {
363
450
let result =
364
451
self . review_output ( current_output, device_address, temp_data. read_all ( ) ) ;
365
- self . reset_buffer ( ) ;
452
+ self . reset_buffer ( self . token_metadata_length ) ;
366
453
result
367
454
} else {
368
455
Ok ( ( ) )
@@ -497,6 +584,7 @@ pub struct OutputIndexes {
497
584
}
498
585
499
586
pub struct TokenIndexes {
587
+ pub has_token_metadata : bool ,
500
588
pub token_id : ( usize , usize ) ,
501
589
pub token_amount : ( usize , usize ) ,
502
590
}
@@ -506,6 +594,15 @@ fn bytes_to_string(bytes: &[u8]) -> Result<&str, ErrorCode> {
506
594
from_utf8 ( bytes) . map_err ( |_| ErrorCode :: InternalError )
507
595
}
508
596
597
+ #[ inline]
598
+ fn get_token_symbol_bytes ( bytes : & [ u8 ] ) -> & [ u8 ] {
599
+ let mut index = 0 ;
600
+ while index < bytes. len ( ) && bytes[ index] != 0 {
601
+ index += 1 ;
602
+ }
603
+ & bytes[ ..index]
604
+ }
605
+
509
606
fn review < ' a > ( fields : & ' a [ Field < ' a > ] , review_message : & str ) -> Result < ( ) , ErrorCode > {
510
607
#[ cfg( not( any( target_os = "stax" , target_os = "flex" ) ) ) ]
511
608
{
0 commit comments