diff --git a/src/handle_swap_sign_transaction.c b/src/handle_swap_sign_transaction.c index 24bb2ed..63a6c36 100644 --- a/src/handle_swap_sign_transaction.c +++ b/src/handle_swap_sign_transaction.c @@ -61,6 +61,7 @@ void handle_swap_sign_transaction(chain_config_t* config) { storage.contractDetails = 0x00; storage.initialized = 0x01; storage.displayNonce = 0x00; + storage.contractDetails = 0x00; nvm_write((void*) &N_storage, (void*) &storage, sizeof(internalStorage_t)); } diff --git a/src/main.c b/src/main.c index e5344eb..d366361 100644 --- a/src/main.c +++ b/src/main.c @@ -49,7 +49,6 @@ strings_t strings; cx_sha3_t global_sha3; uint8_t appState; -bool dataPresent; bool called_from_swap; bool externalPluginIsSet; #ifdef HAVE_STARKWARE @@ -779,6 +778,7 @@ void coin_main(chain_config_t *coin_config) { #endif storage.contractDetails = 0x00; storage.displayNonce = 0x00; + storage.displayFeeDetails = 0x00; storage.initialized = 0x01; nvm_write((void *) &N_storage, (void *) &storage, sizeof(internalStorage_t)); } diff --git a/src/shared_context.h b/src/shared_context.h index f36c131..7b35b21 100644 --- a/src/shared_context.h +++ b/src/shared_context.h @@ -30,6 +30,7 @@ typedef struct internalStorage_t { unsigned char dataAllowed; unsigned char contractDetails; unsigned char displayNonce; + unsigned char displayFeeDetails; uint8_t initialized; } internalStorage_t; @@ -165,7 +166,8 @@ typedef enum { typedef struct txStringProperties_t { char fullAddress[43]; char fullAmount[50]; - char maxFee[50]; + char maxFee[50]; // Used as BaseFee when detailing fees + char priorityFee[50]; char nonce[8]; // 10M tx per account ought to be enough for everybody char chainID[8]; // 10M different chainID ought to be enough for people to find a unique // chainID for their token / chain. @@ -195,7 +197,6 @@ extern cx_sha3_t global_sha3; extern const internalStorage_t N_storage_real; extern bool called_from_swap; -extern bool dataPresent; extern bool externalPluginIsSet; extern uint8_t appState; #ifdef HAVE_STARKWARE diff --git a/src/ui_flow.c b/src/ui_flow.c index 6bac181..67d257b 100644 --- a/src/ui_flow.c +++ b/src/ui_flow.c @@ -5,6 +5,7 @@ void display_settings(const ux_flow_step_t* const start_step); void switch_settings_contract_data(void); void switch_settings_display_data(void); void switch_settings_display_nonce(void); +void switch_settings_display_fee_details(void); ////////////////////////////////////////////////////////////////////// // clang-format off @@ -78,6 +79,15 @@ UX_STEP_CB( .text = strings.common.fullAddress + 26 }); +UX_STEP_CB( + ux_settings_flow_4_step, + bnnn_paging, + switch_settings_display_fee_details(), + { + .title = "Fee Details", + .text = strings.common.fullAddress + 40 + }); + #else UX_STEP_CB( @@ -113,10 +123,21 @@ UX_STEP_CB( strings.common.fullAddress + 26 }); + UX_STEP_CB( + ux_settings_flow_4_step, + bnnn, + switch_settings_display_fee_details(), + { + "Fee Details", + "Display fee details", + "when possible", + strings.common.fullAddress + 40 + }); + #endif UX_STEP_CB( - ux_settings_flow_4_step, + ux_settings_flow_5_step, pb, ui_idle(), { @@ -129,7 +150,8 @@ UX_FLOW(ux_settings_flow, &ux_settings_flow_1_step, &ux_settings_flow_2_step, &ux_settings_flow_3_step, - &ux_settings_flow_4_step); + &ux_settings_flow_4_step, + &ux_settings_flow_5_step); void display_settings(const ux_flow_step_t* const start_step) { strcpy(strings.common.fullAddress, (N_storage.dataAllowed ? "Allowed" : "NOT Allowed")); @@ -137,6 +159,8 @@ void display_settings(const ux_flow_step_t* const start_step) { (N_storage.contractDetails ? "Displayed" : "NOT Displayed")); strcpy(strings.common.fullAddress + 26, (N_storage.displayNonce ? "Displayed" : "NOT Displayed")); + strcpy(strings.common.fullAddress + 40, + (N_storage.displayFeeDetails ? "Displayed" : "NOT Displayed")); ux_flow_init(0, ux_settings_flow, start_step); } @@ -157,3 +181,9 @@ void switch_settings_display_nonce() { nvm_write((void*) &N_storage.displayNonce, (void*) &value, sizeof(uint8_t)); display_settings(&ux_settings_flow_3_step); } + +void switch_settings_display_fee_details() { + uint8_t value = (N_storage.displayFeeDetails ? 0 : 1); + nvm_write((void*) &N_storage.displayFeeDetails, (void*) &value, sizeof(uint8_t)); + display_settings(&ux_settings_flow_4_step); +} \ No newline at end of file diff --git a/src_common/ethUstream.c b/src_common/ethUstream.c index dc57814..7f1643c 100644 --- a/src_common/ethUstream.c +++ b/src_common/ethUstream.c @@ -179,14 +179,12 @@ static void processMaxPriorityFeePerGas(txContext_t *context) { THROW(EXCEPTION); } if (context->currentFieldPos < context->currentFieldLength) { - uint8_t tmp[100]; uint32_t copySize = MIN(context->commandLength, context->currentFieldLength - context->currentFieldPos); - // copyTxData(context, NULL, copySize); - copyTxData(context, &tmp, copySize); + copyTxData(context, context->content->maxPriorityFeePerGas.value + context->currentFieldPos, copySize); } if (context->currentFieldPos == context->currentFieldLength) { - // context->content->nonce.length = context->currentFieldLength; + context->content->maxPriorityFeePerGas.length = context->currentFieldLength; context->currentField++; context->processingField = false; } @@ -286,6 +284,26 @@ static void processData(txContext_t *context) { PRINTF("Invalid type for RLP_DATA\n"); THROW(EXCEPTION); } + if (context->currentFieldPos < context->currentFieldLength) { + uint32_t copySize = + MIN(context->commandLength, context->currentFieldLength - context->currentFieldPos); + // If there is no data, set dataPresent to false. + if (copySize == 1 && *context->workBuffer == 0x00) { + context->content->dataPresent = false; + } + copyTxData(context, NULL, copySize); + } + if (context->currentFieldPos == context->currentFieldLength) { + context->currentField++; + context->processingField = false; + } +} + +static void processAndDiscard(txContext_t *context) { + if (context->currentFieldIsList) { + PRINTF("Invalid type for Discarded field\n"); + THROW(EXCEPTION); + } if (context->currentFieldPos < context->currentFieldLength) { uint32_t copySize = MIN(context->commandLength, context->currentFieldLength - context->currentFieldPos); @@ -319,7 +337,6 @@ static void processV(txContext_t *context) { } static bool processEIP1559Tx(txContext_t *context) { - PRINTF("1559\n"); switch (context->currentField) { case EIP1559_RLP_CONTENT: { processContent(context); @@ -375,7 +392,7 @@ static bool processEIP1559Tx(txContext_t *context) { } case EIP1559_RLP_SENDER_R: case EIP1559_RLP_SENDER_S: - processData(context); + processAndDiscard(context); break; default: PRINTF("Invalid RLP decoder context\n"); @@ -417,14 +434,15 @@ static bool processEIP2930Tx(txContext_t *context) { case EIP2930_RLP_YPARITY: processV(context); break; + case EIP2930_RLP_DATA: + processData(context); + break; case EIP2930_RLP_ACCESS_LIST: processAccessList(context); break; - case EIP2930_RLP_DATA: case EIP2930_RLP_SENDER_R: case EIP2930_RLP_SENDER_S: - processData(context); - PRINTF("DONE\n"); + processAndDiscard(context); break; default: PRINTF("Invalid RLP decoder context\n"); @@ -461,9 +479,11 @@ static bool processLegacyTx(txContext_t *context) { processValue(context); break; case LEGACY_RLP_DATA: + processData(context); + break; case LEGACY_RLP_R: case LEGACY_RLP_S: - processData(context); + processAndDiscard(context); break; case LEGACY_RLP_V: processV(context); diff --git a/src_common/ethUstream.h b/src_common/ethUstream.h index 03f7de2..41fcedf 100644 --- a/src_common/ethUstream.h +++ b/src_common/ethUstream.h @@ -83,8 +83,8 @@ typedef enum rlpEIP2930TxField_e { typedef enum rlpEIP1559TxField_e { EIP1559_RLP_NONE = RLP_NONE, - EIP1559_RLP_TYPE, // For wanchain EIP1559_RLP_CONTENT, + EIP1559_RLP_TYPE, // For wanchain EIP1559_RLP_CHAINID, EIP1559_RLP_NONCE, EIP1559_RLP_MAX_PRIORITY_FEE_PER_GAS, @@ -125,8 +125,9 @@ typedef struct txInt256_t { } txInt256_t; typedef struct txContent_t { - txInt256_t gasprice; - txInt256_t startgas; + txInt256_t gasprice; // Used as MaxFeePerGas when dealing with EIP1559 transactions. + txInt256_t startgas; // Also known as `gasLimit`. + txInt256_t maxPriorityFeePerGas; txInt256_t value; txInt256_t nonce; txInt256_t chainID; @@ -134,6 +135,7 @@ typedef struct txContent_t { uint8_t destinationLength; uint8_t v[4]; uint8_t vLength; + bool dataPresent; } txContent_t; typedef struct txContext_t { diff --git a/src_features/signTx/cmd_signTx.c b/src_features/signTx/cmd_signTx.c index 6db4f4a..c429992 100644 --- a/src_features/signTx/cmd_signTx.c +++ b/src_features/signTx/cmd_signTx.c @@ -38,7 +38,7 @@ void handleSign(uint8_t p1, workBuffer += 4; dataLength -= 4; } - dataPresent = false; + tmpContent.txContent.dataPresent = false; dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_UNAVAILABLE; // EIP 2718: TransactionType might be present before the TransactionPayload. @@ -89,7 +89,6 @@ void handleSign(uint8_t p1, THROW(0x6A80); } - PRINTF("FINALIZE\n"); if (txResult == USTREAM_FINISHED) { finalizeParsing(false); } diff --git a/src_features/signTx/logic_signTx.c b/src_features/signTx/logic_signTx.c index 344c72c..4c41b1f 100644 --- a/src_features/signTx/logic_signTx.c +++ b/src_features/signTx/logic_signTx.c @@ -32,7 +32,7 @@ customStatus_e customProcessor(txContext_t *context) { (context->txType == EIP2930 && context->currentField == EIP2930_RLP_DATA) || (context->txType == EIP1559 && context->currentField == EIP1559_RLP_DATA)) && (context->currentFieldLength != 0)) { - dataPresent = true; + context->content->dataPresent = true; // If handling a new contract rather than a function call, abort immediately if (tmpContent.txContent.destinationLength == 0) { return CUSTOM_NOT_HANDLED; @@ -194,27 +194,31 @@ void reportFinalizeError(bool direct) { } } -void computeFees(char *displayBuffer, uint32_t displayBufferSize) { - uint256_t gasPrice, startGas, uint256; +void computeFees(const txInt256_t *BEgasPrice, const txInt256_t *BEgasLimit, uint256_t *output) { + uint256_t gasPrice = {0}; + uint256_t gasLimit = {0}; + + PRINTF("Gas price %.*H\n", + BEgasPrice->length, + BEgasPrice->value); + PRINTF("Gas limit %.*H\n", + BEgasLimit->length, + BEgasLimit->value); + convertUint256BE(BEgasPrice->value, + BEgasPrice->length, + &gasPrice); + convertUint256BE(BEgasLimit->value, + BEgasLimit->length, + &gasLimit); + mul256(&gasPrice, &gasLimit, output); +} + +void feesToString(const uint256_t *rawFee, char *displayBuffer, uint32_t displayBufferSize) { uint8_t *feeTicker = (uint8_t *) PIC(chainConfig->coinName); uint8_t tickerOffset = 0; uint32_t i; - PRINTF("Max fee\n"); - PRINTF("Gasprice %.*H\n", - tmpContent.txContent.gasprice.length, - tmpContent.txContent.gasprice.value); - PRINTF("Startgas %.*H\n", - tmpContent.txContent.startgas.length, - tmpContent.txContent.startgas.value); - convertUint256BE(tmpContent.txContent.gasprice.value, - tmpContent.txContent.gasprice.length, - &gasPrice); - convertUint256BE(tmpContent.txContent.startgas.value, - tmpContent.txContent.startgas.length, - &startGas); - mul256(&gasPrice, &startGas, &uint256); - tostring256(&uint256, 10, (char *) (G_io_apdu_buffer + 100), 100); + tostring256(rawFee, 10, (char *) (G_io_apdu_buffer + 100), 100); i = 0; while (G_io_apdu_buffer[100 + i]) { i++; @@ -236,6 +240,38 @@ void computeFees(char *displayBuffer, uint32_t displayBufferSize) { i++; } displayBuffer[tickerOffset + i] = '\0'; + PRINTF("Displayed fees: %s\n", displayBuffer); +} + +// Compute the fees, transform it to a string, prepend a ticker to it and copy everything to `displayBuffer`. +void prepareFees(const txInt256_t *BEGasPrice, const txInt256_t *BEGasLimit, char *displayBuffer, uint32_t displayBufferSize) { + uint256_t rawFee = {0}; + computeFees(BEGasPrice, BEGasLimit, &rawFee); + feesToString(&rawFee, displayBuffer, displayBufferSize); +} + +void prepareAndCopyDetailedFees(char *displayBuffer, uint32_t displayBufferSize) { + uint256_t rawPriorityFee = {0}; + uint256_t rawMaxFee = {0}; + uint256_t rawBaseFee = {0}; + + // Compute the priorityFee and the maxFee. + computeFees(&tmpContent.txContent.maxPriorityFeePerGas, &tmpContent.txContent.startgas, &rawPriorityFee); + computeFees(&tmpContent.txContent.gasprice, &tmpContent.txContent.startgas, &rawMaxFee); + // Substract priorityFee from maxFee -> this is the baseFee + minus256(&rawMaxFee, &rawPriorityFee, &rawBaseFee); + + // Transform priorityFee to string (with a ticker). + PRINTF("Computing priority fee\n"); + feesToString(&rawPriorityFee, displayBuffer, displayBufferSize); + // Copy it to destination. + compareOrCopy(strings.common.priorityFee, displayBuffer, called_from_swap); + + PRINTF("Computing base fee\n"); + // Transform priorityFee to string (with a ticker). + feesToString(&rawBaseFee, displayBuffer, displayBufferSize); + // Copy it to destination. + compareOrCopy(strings.common.maxFee, displayBuffer, called_from_swap); } void finalizeParsing(bool direct) { @@ -279,7 +315,6 @@ void finalizeParsing(bool direct) { 32); // Finalize the plugin handling - PRINTF("1\n"); if (dataContext.tokenContext.pluginStatus >= ETH_PLUGIN_RESULT_SUCCESSFUL) { genericUI = false; eth_plugin_prepare_finalize(&pluginFinalize); @@ -322,7 +357,7 @@ void finalizeParsing(bool direct) { // Handle the right interface switch (pluginFinalize.uiType) { case ETH_UI_TYPE_GENERIC: - dataPresent = false; + tmpContent.txContent.dataPresent = false; // Add the number of screens + the number of additional screens to get the total // number of screens needed. dataContext.tokenContext.pluginUiMaxItems = @@ -330,7 +365,7 @@ void finalizeParsing(bool direct) { break; case ETH_UI_TYPE_AMOUNT_ADDRESS: genericUI = true; - dataPresent = false; + tmpContent.txContent.dataPresent = false; if ((pluginFinalize.amount == NULL) || (pluginFinalize.address == NULL)) { PRINTF("Incorrect amount/address set by plugin\n"); reportFinalizeError(direct); @@ -359,8 +394,7 @@ void finalizeParsing(bool direct) { } } - PRINTF("2\n"); - if (dataPresent && !N_storage.dataAllowed) { + if (tmpContent.txContent.dataPresent && !N_storage.dataAllowed) { reportFinalizeError(direct); if (!direct) { return; @@ -401,8 +435,12 @@ void finalizeParsing(bool direct) { } // Compute maximum fee if (genericUI) { - computeFees(displayBuffer, sizeof(displayBuffer)); - compareOrCopy(strings.common.maxFee, displayBuffer, called_from_swap); + if (N_storage.displayFeeDetails) { + prepareAndCopyDetailedFees(displayBuffer, sizeof(displayBuffer)); + } else { + prepareFees(&tmpContent.txContent.gasprice, &tmpContent.txContent.startgas, displayBuffer, sizeof(displayBuffer)); + compareOrCopy(strings.common.maxFee, displayBuffer, called_from_swap); + } } // Prepare chainID field @@ -429,7 +467,6 @@ void finalizeParsing(bool direct) { return; } } - PRINTF("3\n"); bool no_consent; @@ -443,7 +480,7 @@ void finalizeParsing(bool direct) { io_seproxyhal_touch_tx_ok(NULL); } else { if (genericUI) { - ux_approve_tx(dataPresent); + ux_approve_tx(tmpContent.txContent.dataPresent); } else { plugin_ui_start(); } diff --git a/src_features/signTx/ui_flow_signTx.c b/src_features/signTx/ui_flow_signTx.c index 3b7c924..f88e34a 100644 --- a/src_features/signTx/ui_flow_signTx.c +++ b/src_features/signTx/ui_flow_signTx.c @@ -108,6 +108,20 @@ UX_STEP_NOCB( .title = "Address", .text = strings.common.fullAddress, }); +UX_STEP_NOCB( + ux_approval_base_fee_step, + bnnn_paging, + { + .title = "Base Fee", + .text = strings.common.maxFee, + }); +UX_STEP_NOCB( + ux_approval_priority_fee_step, + bnnn_paging, + { + .title = "Priority Fee", + .text = strings.common.priorityFee, + }); UX_STEP_NOCB( ux_approval_fees_step, bnnn_paging, @@ -157,18 +171,18 @@ UX_STEP_NOCB(ux_approval_data_warning_step, }); // clang-format on -const ux_flow_step_t *ux_approval_tx_flow_[10]; +const ux_flow_step_t *ux_approval_tx_flow[12]; void ux_approve_tx(bool dataPresent) { int step = 0; - ux_approval_tx_flow_[step++] = &ux_approval_review_step; - if (dataPresent && !N_storage.contractDetails) { - ux_approval_tx_flow_[step++] = &ux_approval_data_warning_step; + ux_approval_tx_flow[step++] = &ux_approval_review_step; + if (tmpContent.txContent.dataPresent && !N_storage.contractDetails) { + ux_approval_tx_flow[step++] = &ux_approval_data_warning_step; } - ux_approval_tx_flow_[step++] = &ux_approval_amount_step; - ux_approval_tx_flow_[step++] = &ux_approval_address_step; + ux_approval_tx_flow[step++] = &ux_approval_amount_step; + ux_approval_tx_flow[step++] = &ux_approval_address_step; if (N_storage.displayNonce) { - ux_approval_tx_flow_[step++] = &ux_approval_nonce_step; + ux_approval_tx_flow[step++] = &ux_approval_nonce_step; } uint32_t id; @@ -182,12 +196,17 @@ void ux_approve_tx(bool dataPresent) { THROW(0x6501); } if (id != ETHEREUM_MAINNET_CHAINID) { - ux_approval_tx_flow_[step++] = &ux_approval_chainid_step; + ux_approval_tx_flow[step++] = &ux_approval_chainid_step; } - ux_approval_tx_flow_[step++] = &ux_approval_fees_step; - ux_approval_tx_flow_[step++] = &ux_approval_accept_step; - ux_approval_tx_flow_[step++] = &ux_approval_reject_step; - ux_approval_tx_flow_[step++] = FLOW_END_STEP; + if (txContext.txType == EIP1559 && N_storage.displayFeeDetails) { + ux_approval_tx_flow[step++] = &ux_approval_base_fee_step; + ux_approval_tx_flow[step++] = &ux_approval_priority_fee_step; + } else { + ux_approval_tx_flow[step++] = &ux_approval_fees_step; + } + ux_approval_tx_flow[step++] = &ux_approval_accept_step; + ux_approval_tx_flow[step++] = &ux_approval_reject_step; + ux_approval_tx_flow[step++] = FLOW_END_STEP; - ux_flow_init(0, ux_approval_tx_flow_, NULL); + ux_flow_init(0, ux_approval_tx_flow, NULL); } \ No newline at end of file