Skip to content

Commit 04c6ce6

Browse files
NBGL GCS UI implementation
1 parent f7960b5 commit 04c6ce6

File tree

2 files changed

+227
-1
lines changed

2 files changed

+227
-1
lines changed

glyphs/review_info_button.gif

538 Bytes
Loading

src_nbgl/ui_gcs.c

Lines changed: 227 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,235 @@
11
#ifdef HAVE_GENERIC_TX_PARSER
22

3-
void ui_gcs(void) {
3+
#include <string.h>
4+
#include "ui_nbgl.h"
5+
#include "gtp_tx_info.h"
6+
#include "gtp_field_table.h"
7+
#include "mem.h"
8+
#include "mem_utils.h"
9+
#include "network.h"
10+
#include "ui_callbacks.h"
11+
#include "feature_signTx.h"
12+
#include "apdu_constants.h"
13+
14+
static nbgl_layoutTagValueList_t g_pair_list;
15+
static size_t g_alloc_size;
16+
17+
static void review_choice(bool confirm) {
18+
if (confirm) {
19+
io_seproxyhal_touch_tx_ok();
20+
nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_SIGNED, ui_idle);
21+
} else {
22+
io_seproxyhal_touch_tx_cancel();
23+
nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_REJECTED, ui_idle);
24+
}
25+
}
26+
27+
static char *_strdup(const char *src) {
28+
char *dst;
29+
size_t length = strlen(src) + 1;
30+
31+
if ((dst = mem_alloc(length)) != NULL) {
32+
memmove(dst, src, length);
33+
}
34+
return dst;
35+
}
36+
37+
static bool cleanup_on_error(const void *mem_before) {
38+
mem_dealloc(mem_alloc(0) - mem_before);
39+
return false;
40+
}
41+
42+
#define MAX_INFO_COUNT 3
43+
44+
static bool prepare_infos(nbgl_contentInfoList_t *infos) {
45+
char *tmp_buf = strings.tmp.tmp;
46+
size_t tmp_buf_size = sizeof(strings.tmp.tmp);
47+
size_t off;
48+
uint8_t count = 0;
49+
const char **keys;
50+
const char **values;
51+
nbgl_contentValueExt_t *extensions;
52+
const char *value;
53+
int contract_idx = -1;
54+
55+
if (((keys = mem_alloc_and_align(sizeof(*keys) * MAX_INFO_COUNT, __alignof__(*keys))) ==
56+
NULL) ||
57+
((values = mem_alloc_and_align(sizeof(*values) * MAX_INFO_COUNT, __alignof__(*values))) ==
58+
NULL)) {
59+
return false;
60+
}
61+
if ((value = get_creator_legal_name()) != NULL) {
62+
snprintf(tmp_buf, tmp_buf_size, "Contract owner");
63+
if ((keys[count] = _strdup(tmp_buf)) == NULL) {
64+
return false;
65+
}
66+
snprintf(tmp_buf, tmp_buf_size, "%s", value);
67+
if ((value = get_creator_url()) != NULL) {
68+
off = strlen(tmp_buf);
69+
snprintf(tmp_buf + off, tmp_buf_size - off, "\n%s", value);
70+
}
71+
if ((values[count] = _strdup(tmp_buf)) == NULL) {
72+
return false;
73+
}
74+
count += 1;
75+
}
76+
77+
if ((value = get_contract_name()) != NULL) {
78+
snprintf(tmp_buf, tmp_buf_size, "Contract");
79+
if ((keys[count] = _strdup(tmp_buf)) == NULL) {
80+
return false;
81+
}
82+
snprintf(tmp_buf, tmp_buf_size, "%s", value);
83+
if ((values[count] = _strdup(tmp_buf)) == NULL) {
84+
return false;
85+
}
86+
contract_idx = count;
87+
count += 1;
88+
}
89+
90+
if ((value = get_deploy_date()) != NULL) {
91+
snprintf(tmp_buf, tmp_buf_size, "Deployed on");
92+
if ((keys[count] = _strdup(tmp_buf)) == NULL) {
93+
return false;
94+
}
95+
snprintf(tmp_buf, tmp_buf_size, "%s", value);
96+
if ((values[count] = _strdup(tmp_buf)) == NULL) {
97+
return false;
98+
}
99+
count += 1;
100+
}
101+
102+
if ((extensions = mem_alloc_and_align(sizeof(*extensions) * count, __alignof__(*extensions))) ==
103+
NULL) {
104+
return false;
105+
}
106+
explicit_bzero(extensions, sizeof(*extensions) * count);
107+
108+
if (contract_idx != -1) {
109+
if (!getEthDisplayableAddress((uint8_t *) get_contract_addr(),
110+
tmp_buf,
111+
tmp_buf_size,
112+
chainConfig->chainId)) {
113+
return false;
114+
}
115+
if ((extensions[contract_idx].title = _strdup(tmp_buf)) == NULL) {
116+
return false;
117+
}
118+
// Etherscan only for mainnet
119+
if (get_tx_chain_id() == ETHEREUM_MAINNET_CHAINID) {
120+
if ((extensions[contract_idx].explanation = _strdup("Scan to view on Etherscan")) ==
121+
NULL) {
122+
return false;
123+
}
124+
snprintf(tmp_buf,
125+
tmp_buf_size,
126+
"https://etherscan.io/address/%s",
127+
extensions[contract_idx].title);
128+
if ((extensions[contract_idx].fullValue = _strdup(tmp_buf)) == NULL) {
129+
return false;
130+
}
131+
} else {
132+
extensions[contract_idx].fullValue = extensions[contract_idx].title;
133+
}
134+
extensions[contract_idx].aliasType = QR_CODE_ALIAS;
135+
}
136+
137+
infos->nbInfos = count;
138+
infos->infoTypes = keys;
139+
infos->infoContents = values;
140+
infos->infoExtensions = extensions;
141+
infos->withExtensions = true;
142+
return true;
143+
}
144+
145+
bool ui_gcs(void) {
146+
char *tmp_buf = strings.tmp.tmp;
147+
size_t tmp_buf_size = sizeof(strings.tmp.tmp);
148+
size_t off;
149+
const char *review_title;
150+
const char *sign_title;
151+
nbgl_contentTagValue_t *pairs;
152+
s_field_table_entry entry;
153+
bool show_network;
154+
nbgl_tipBox_t tip_box;
155+
const void *mem_before = mem_alloc(0);
156+
157+
snprintf(tmp_buf, tmp_buf_size, "Review transaction to %s", get_operation_type());
158+
if ((review_title = _strdup(tmp_buf)) == NULL) {
159+
return cleanup_on_error(mem_before);
160+
}
161+
snprintf(tmp_buf, tmp_buf_size, "Sign transaction to %s?", get_operation_type());
162+
if ((sign_title = _strdup(tmp_buf)) == NULL) {
163+
return cleanup_on_error(mem_before);
164+
}
165+
explicit_bzero(&tip_box, sizeof(tip_box));
166+
tip_box.icon = &C_review_info_button;
167+
tip_box.text = NULL;
168+
tip_box.modalTitle = "Contract information";
169+
tip_box.type = INFOS_LIST;
170+
if (!prepare_infos(&tip_box.infos)) {
171+
return cleanup_on_error(mem_before);
172+
}
173+
snprintf(tmp_buf, tmp_buf_size, "Interaction with a\nsmart contract");
174+
off = strlen(tmp_buf);
175+
if (get_creator_name()) {
176+
snprintf(tmp_buf + off, tmp_buf_size - off, " from:\n%s", get_creator_name());
177+
}
178+
if ((tip_box.text = _strdup(tmp_buf)) == NULL) {
179+
return cleanup_on_error(mem_before);
180+
}
181+
182+
g_pair_list.nbPairs = field_table_size() + 1;
183+
show_network = get_tx_chain_id() != chainConfig->chainId;
184+
if (show_network) {
185+
g_pair_list.nbPairs += 1;
186+
}
187+
if ((pairs = mem_alloc_and_align(sizeof(*pairs) * g_pair_list.nbPairs, __alignof__(*pairs))) ==
188+
NULL) {
189+
return cleanup_on_error(mem_before);
190+
}
191+
explicit_bzero(pairs, sizeof(*pairs) * g_pair_list.nbPairs);
192+
193+
for (int i = 0; i < (int) field_table_size(); ++i) {
194+
if (!get_from_field_table(i, &entry)) {
195+
return cleanup_on_error(mem_before);
196+
}
197+
pairs[i].item = entry.key;
198+
pairs[i].value = entry.value;
199+
}
200+
201+
if (show_network) {
202+
pairs[g_pair_list.nbPairs - 2].item = _strdup("Network");
203+
if (get_network_as_string(tmp_buf, tmp_buf_size) != APDU_RESPONSE_OK) {
204+
return cleanup_on_error(mem_before);
205+
}
206+
pairs[g_pair_list.nbPairs - 2].value = _strdup(tmp_buf);
207+
}
208+
209+
pairs[g_pair_list.nbPairs - 1].item = _strdup("Max fees");
210+
if (max_transaction_fee_to_string(&tmpContent.txContent.gasprice,
211+
&tmpContent.txContent.startgas,
212+
tmp_buf,
213+
tmp_buf_size) == false) {
214+
PRINTF("Error: Could not format the max fees!\n");
215+
}
216+
pairs[g_pair_list.nbPairs - 1].value = _strdup(tmp_buf);
217+
218+
g_pair_list.pairs = pairs;
219+
nbgl_useCaseAdvancedReview(TYPE_TRANSACTION,
220+
&g_pair_list,
221+
get_tx_icon(),
222+
review_title,
223+
NULL,
224+
sign_title,
225+
&tip_box,
226+
review_choice);
227+
g_alloc_size = mem_alloc(0) - mem_before;
228+
return true;
4229
}
5230

6231
void ui_gcs_cleanup(void) {
232+
mem_dealloc(g_alloc_size);
7233
}
8234

9235
#endif // HAVE_GENERIC_TX_PARSER

0 commit comments

Comments
 (0)