@@ -28,6 +28,44 @@ static tz_parser_result push_frame(tz_parser_state *state,
2828 tz_operation_parser_step_kind step );
2929static tz_parser_result pop_frame (tz_parser_state * state );
3030
31+ typedef struct {
32+ const char name [32 ];
33+ const char symbol [12 ];
34+ uint8_t decimals ;
35+ const uint8_t contract_hash [20 ];
36+ } fa2_token_metadata_t ;
37+
38+ static const fa2_token_metadata_t FA2_TOKEN_REGISTRY [] = {
39+ /* KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT */
40+ {"FA2 Token" ,
41+ "FA2" ,
42+ 6 ,
43+ {0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
44+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
45+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
46+ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }},
47+ };
48+
49+ #define FA2_TOKEN_REGISTRY_SIZE \
50+ (sizeof(FA2_TOKEN_REGISTRY) / sizeof(FA2_TOKEN_REGISTRY[0]))
51+
52+ static const fa2_token_metadata_t *
53+ fa2_find_token (const uint8_t * destination_22bytes )
54+ {
55+ if (destination_22bytes [0 ] != 1 ) {
56+ return NULL ;
57+ }
58+ for (size_t i = 0 ; i < FA2_TOKEN_REGISTRY_SIZE ; i ++ ) {
59+ if (memcmp (destination_22bytes + 1 ,
60+ FA2_TOKEN_REGISTRY [i ].contract_hash ,
61+ 20 )
62+ == 0 ) {
63+ return & FA2_TOKEN_REGISTRY [i ];
64+ }
65+ }
66+ return NULL ;
67+ }
68+
3169#ifdef TEZOS_DEBUG
3270const char * const tz_operation_parser_step_name [] = {"OPTION" ,
3371 "TUPLE" ,
@@ -605,6 +643,107 @@ tz_step_tag(tz_parser_state *state)
605643#define FA2_TO_ADDR_OFS (TZ_CAPTURE_BUFFER_SIZE / 2)
606644#define FA2_ADDR_MAX_LEN (TZ_CAPTURE_BUFFER_SIZE / 2 - 1)
607645
646+ /**
647+ * @brief Format an integer token amount string with token decimals and symbol.
648+ *
649+ * @param str Decimal ASCII digits buffer (in/out, NUL-terminated).
650+ * @param buf_size Size of @p str (including the trailing NUL byte).
651+ * @param decimals Number of fractional digits for the token.
652+ * @param symbol Optional token symbol appended as " <symbol>" when it fits.
653+ *
654+ * Formatting is done in three phases:
655+ * - left-pad with zeros when the integer has fewer than decimals+1 digits,
656+ * - insert a decimal separator and trim trailing fractional zeros,
657+ * - append the token symbol if enough space remains.
658+ *
659+ * The function never writes past @p buf_size. If the symbol would not fit, it
660+ * is skipped and only the formatted amount is kept.
661+ */
662+ static void
663+ tz_format_token_amount (char * str ,
664+ size_t buf_size ,
665+ uint8_t decimals ,
666+ const char * symbol )
667+ {
668+ size_t len ;
669+
670+ if ((str == NULL ) || (buf_size == 0 )) {
671+ return ;
672+ }
673+
674+ len = strlen (str );
675+ if (len >= buf_size ) {
676+ str [buf_size - 1 ] = 0 ;
677+ len = buf_size - 1 ;
678+ }
679+
680+ if (len == 0 ) {
681+ if (buf_size < 2 ) {
682+ str [0 ] = 0 ;
683+ return ;
684+ }
685+ str [0 ] = '0' ;
686+ str [1 ] = 0 ;
687+ len = 1 ;
688+ }
689+
690+ if (decimals > 0 ) {
691+ size_t frac_digits = (size_t )decimals ;
692+
693+ /* Ensure at least decimals+1 integer digits by left-padding with '0'. */
694+ if (len <= frac_digits ) {
695+ int pad = (int )(frac_digits + 1U - len );
696+ if ((len + (size_t )pad + 1U ) > buf_size ) {
697+ return ;
698+ }
699+ for (int j = (int )len ; j >= 0 ; j -- ) {
700+ str [j + pad ] = str [j ];
701+ }
702+ for (int j = 0 ; j < pad ; j ++ ) {
703+ str [j ] = '0' ;
704+ }
705+ len += (size_t )pad ;
706+ }
707+
708+ /* Detect whether the whole fractional part is zero. */
709+ int no_decimals = 1 ;
710+ for (size_t i = 0 ; i < frac_digits ; i ++ ) {
711+ no_decimals &= (str [len - 1 - i ] == '0' );
712+ }
713+ if (no_decimals ) {
714+ str [len - frac_digits ] = 0 ;
715+ len -= frac_digits ;
716+ } else {
717+ /* Insert '.', then trim trailing fractional zeros and a trailing '.'. */
718+ if ((len + 1U ) >= buf_size ) {
719+ return ;
720+ }
721+ for (size_t i = 0 ; i < frac_digits ; i ++ ) {
722+ str [len - i ] = str [len - i - 1 ];
723+ }
724+ str [len - frac_digits ] = '.' ;
725+ len ++ ;
726+ str [len ] = 0 ;
727+ while ((len > 0 ) && (str [len - 1 ] == '0' )) {
728+ len -- ;
729+ str [len ] = 0 ;
730+ }
731+ if ((len > 0 ) && (str [len - 1 ] == '.' )) {
732+ len -- ;
733+ str [len ] = 0 ;
734+ }
735+ }
736+ }
737+
738+ if ((symbol != NULL ) && symbol [0 ]) {
739+ size_t symbol_len = strlen (symbol );
740+ if ((len + 1U + symbol_len + 1U ) <= buf_size ) {
741+ strlcat (str , " " , buf_size );
742+ strlcat (str , symbol , buf_size );
743+ }
744+ }
745+ }
746+
608747/**
609748 * @brief Switch FA2 parser to binary fallback for remaining bytes
610749 *
@@ -952,7 +1091,17 @@ tz_step_read_fa2_transfer(tz_parser_state *state)
9521091 if (regs -> oofs > 0 ) {
9531092 tz_stop (IM_FULL );
9541093 }
955- STRLCPY (state -> field_info .field_name , "Token Amount" );
1094+ if (op -> frame -> step_read_fa2 .token_idx >= 0 ) {
1095+ const fa2_token_metadata_t * token
1096+ = & FA2_TOKEN_REGISTRY [op -> frame -> step_read_fa2 .token_idx ];
1097+ tz_format_token_amount ((char * )state -> buffers .num .decimal ,
1098+ sizeof (state -> buffers .num .decimal ),
1099+ token -> decimals ,
1100+ token -> symbol );
1101+ STRLCPY (state -> field_info .field_name , token -> name );
1102+ } else {
1103+ STRLCPY (state -> field_info .field_name , "Token Amount" );
1104+ }
9561105 state -> field_info .is_field_complex = false;
9571106 state -> field_info .field_index ++ ;
9581107 /* Pop the FA2 frame first, then push PRINT so PRINT pops to parent */
@@ -1544,12 +1693,19 @@ tz_step_field(tz_parser_state *state)
15441693 }
15451694 case TZ_OPERATION_FIELD_EXPR : {
15461695 if (op -> is_fa2_candidate && !field -> skip ) {
1696+ const fa2_token_metadata_t * token ;
15471697 op -> frame -> step = TZ_OPERATION_STEP_READ_FA2_TRANSFER ;
15481698 op -> frame -> step_read_fa2 .sub_step = FA2_STEP_OUTER_SEQ_TAG ;
15491699 op -> frame -> step_read_fa2 .addr_ofs = 0 ;
15501700 op -> frame -> step_read_fa2 .size_ofs = 0 ;
15511701 op -> frame -> step_read_fa2 .size_val = 0 ;
15521702 op -> frame -> step_read_fa2 .addr_len = 0 ;
1703+ op -> frame -> step_read_fa2 .token_idx = -1 ;
1704+ token = fa2_find_token (op -> destination );
1705+ if (token != NULL ) {
1706+ op -> frame -> step_read_fa2 .token_idx
1707+ = (int8_t )(token - FA2_TOKEN_REGISTRY );
1708+ }
15531709 } else {
15541710 op -> frame -> step = TZ_OPERATION_STEP_READ_MICHELINE ;
15551711 op -> frame -> step_read_micheline .inited = 0 ;
0 commit comments