Skip to content

Commit f715d4b

Browse files
author
Paulo Oliveira
committed
feat: format token amount due to token info
1 parent e813dd0 commit f715d4b

2 files changed

Lines changed: 158 additions & 1 deletion

File tree

app/src/parser/operation_parser.c

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,44 @@ static tz_parser_result push_frame(tz_parser_state *state,
2828
tz_operation_parser_step_kind step);
2929
static 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
3270
const 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;

app/src/parser/operation_state.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ typedef struct {
233233
uint32_t size_val; /// accumulator for 4-byte size
234234
uint16_t addr_len; /// remaining bytes for address
235235
tz_num_parser_regs num_state; /// num parser state for amount
236+
int8_t token_idx; /// matched token index, -1 if unknown
236237
} step_read_fa2; /// TZ_OPERATION_STEP_READ_FA2_TRANSFER
237238
};
238239
} tz_operation_parser_frame;

0 commit comments

Comments
 (0)