From 83a44e8ece0519071c7f72febdc8c7e2da9b7c9c Mon Sep 17 00:00:00 2001 From: Ujjwal Kumar <31813384+Ujjwal0501@users.noreply.github.com> Date: Sat, 2 Sep 2023 17:17:49 +0530 Subject: [PATCH] chore (app): Remove unreliable details from txn meta --- apps/evm_family/arbitrum/arbitrum_app.c | 25 +- apps/evm_family/avalanche/avalanche_app.c | 25 +- apps/evm_family/bsc/bsc_app.c | 25 +- apps/evm_family/eth/eth_app.c | 36 +- apps/evm_family/evm_context.h | 4 +- apps/evm_family/evm_contracts.c | 362 ++++++++++++++++++ apps/evm_family/evm_contracts.h | 48 ++- apps/evm_family/evm_sign_txn.c | 38 +- apps/evm_family/evm_txn_helpers.c | 155 ++++---- apps/evm_family/evm_txn_helpers.h | 63 ++- apps/evm_family/evm_user_verification.c | 32 +- apps/evm_family/fantom/fantom_app.c | 25 +- apps/evm_family/optimism/optimism_app.c | 25 +- apps/evm_family/polygon/polygon_app.c | 25 +- common/coin_support/eth.c | 269 ------------- common/coin_support/eth.h | 20 - common/cypherock-common | 2 +- common/proto-options/evm/sign_txn.options | 1 - .../send_transaction_controller_eth.c | 22 +- .../core/tasks/send_transaction_tasks_eth.c | 44 +-- tests/apps/evm_app/evm_txn_tests.c | 8 - 21 files changed, 715 insertions(+), 539 deletions(-) create mode 100644 apps/evm_family/evm_contracts.c diff --git a/apps/evm_family/arbitrum/arbitrum_app.c b/apps/evm_family/arbitrum/arbitrum_app.c index 3c588bfd..8660bf04 100644 --- a/apps/evm_family/arbitrum/arbitrum_app.c +++ b/apps/evm_family/arbitrum/arbitrum_app.c @@ -92,6 +92,20 @@ extern const erc20_contracts_t arbitrum_contracts[]; * STATIC FUNCTION PROTOTYPES *****************************************************************************/ +/** + * @brief Checks if the provided token address is whitelisted and return the + * matching contract instance. + * + * @param address Reference to the buffer containing the token address + * @param contract Pointer to store the matched contract address instance + * + * @return bool Indicating if the provided token address is whitelisted + * @return true If the address matches to an entry in the whitelist + * @return false If the address does not match to an entry in the whitelist + */ +static bool is_token_whitelisted(const uint8_t *address, + const erc20_contracts_t **contract); + /***************************************************************************** * STATIC VARIABLES *****************************************************************************/ @@ -102,8 +116,7 @@ static const evm_config_t arbitrum_app_config = { .chain_id = 42161, // whitelisted contracts - .whitelisted_contracts = NULL, - .whitelist_count = ARBITRUM_WHITELISTED_CONTRACTS_COUNT, + .is_token_whitelisted = is_token_whitelisted, }; static const cy_app_desc_t arbitrum_app_desc = { @@ -124,6 +137,14 @@ static const cy_app_desc_t arbitrum_app_desc = { * STATIC FUNCTIONS *****************************************************************************/ +static bool is_token_whitelisted(const uint8_t *address, + const erc20_contracts_t **contract) { + if (NULL != contract) { + *contract = NULL; + } + return false; +} + /***************************************************************************** * GLOBAL FUNCTIONS *****************************************************************************/ diff --git a/apps/evm_family/avalanche/avalanche_app.c b/apps/evm_family/avalanche/avalanche_app.c index 6df7c741..14041e43 100644 --- a/apps/evm_family/avalanche/avalanche_app.c +++ b/apps/evm_family/avalanche/avalanche_app.c @@ -92,6 +92,20 @@ extern const erc20_contracts_t avalanche_contracts[]; * STATIC FUNCTION PROTOTYPES *****************************************************************************/ +/** + * @brief Checks if the provided token address is whitelisted and return the + * matching contract instance. + * + * @param address Reference to the buffer containing the token address + * @param contract Pointer to store the matched contract address instance + * + * @return bool Indicating if the provided token address is whitelisted + * @return true If the address matches to an entry in the whitelist + * @return false If the address does not match to an entry in the whitelist + */ +static bool is_token_whitelisted(const uint8_t *address, + const erc20_contracts_t **contract); + /***************************************************************************** * STATIC VARIABLES *****************************************************************************/ @@ -102,8 +116,7 @@ static const evm_config_t avalanche_app_config = { .chain_id = 43114, // whitelisted contracts - .whitelisted_contracts = NULL, - .whitelist_count = AVALANCHE_WHITELISTED_CONTRACTS_COUNT, + .is_token_whitelisted = is_token_whitelisted, }; static const cy_app_desc_t avalanche_app_desc = { @@ -125,6 +138,14 @@ static const cy_app_desc_t avalanche_app_desc = { * STATIC FUNCTIONS *****************************************************************************/ +static bool is_token_whitelisted(const uint8_t *address, + const erc20_contracts_t **contract) { + if (NULL != contract) { + *contract = NULL; + } + return false; +} + /***************************************************************************** * GLOBAL FUNCTIONS *****************************************************************************/ diff --git a/apps/evm_family/bsc/bsc_app.c b/apps/evm_family/bsc/bsc_app.c index 44bcc592..8a5a2e28 100644 --- a/apps/evm_family/bsc/bsc_app.c +++ b/apps/evm_family/bsc/bsc_app.c @@ -92,6 +92,20 @@ extern const erc20_contracts_t bsc_contracts[]; * STATIC FUNCTION PROTOTYPES *****************************************************************************/ +/** + * @brief Checks if the provided token address is whitelisted and return the + * matching contract instance. + * + * @param address Reference to the buffer containing the token address + * @param contract Pointer to store the matched contract address instance + * + * @return bool Indicating if the provided token address is whitelisted + * @return true If the address matches to an entry in the whitelist + * @return false If the address does not match to an entry in the whitelist + */ +static bool is_token_whitelisted(const uint8_t *address, + const erc20_contracts_t **contract); + /***************************************************************************** * STATIC VARIABLES *****************************************************************************/ @@ -102,8 +116,7 @@ static const evm_config_t bsc_app_config = { .chain_id = 56, // whitelisted contracts - .whitelisted_contracts = NULL, - .whitelist_count = BSC_WHITELISTED_CONTRACTS_COUNT, + .is_token_whitelisted = is_token_whitelisted, }; static const cy_app_desc_t bsc_app_desc = {.id = 11, @@ -124,6 +137,14 @@ static const cy_app_desc_t bsc_app_desc = {.id = 11, * STATIC FUNCTIONS *****************************************************************************/ +static bool is_token_whitelisted(const uint8_t *address, + const erc20_contracts_t **contract) { + if (NULL != contract) { + *contract = NULL; + } + return false; +} + /***************************************************************************** * GLOBAL FUNCTIONS *****************************************************************************/ diff --git a/apps/evm_family/eth/eth_app.c b/apps/evm_family/eth/eth_app.c index cfa8f107..515935b5 100644 --- a/apps/evm_family/eth/eth_app.c +++ b/apps/evm_family/eth/eth_app.c @@ -90,6 +90,20 @@ extern const erc20_contracts_t eth_contracts[]; * STATIC FUNCTION PROTOTYPES *****************************************************************************/ +/** + * @brief Checks if the provided token address is whitelisted and return the + * matching contract instance. + * + * @param address Reference to the buffer containing the token address + * @param contract Pointer to store the matched contract address instance + * + * @return bool Indicating if the provided token address is whitelisted + * @return true If the address matches to an entry in the whitelist + * @return false If the address does not match to an entry in the whitelist + */ +static bool is_token_whitelisted(const uint8_t *address, + const erc20_contracts_t **contract); + /***************************************************************************** * STATIC VARIABLES *****************************************************************************/ @@ -99,9 +113,7 @@ static const evm_config_t eth_app_config = { .name = "Ethereum", .chain_id = 1, - // whitelisted contracts - .whitelisted_contracts = eth_contracts, - .whitelist_count = ETH_WHITELISTED_CONTRACTS_COUNT, + .is_token_whitelisted = is_token_whitelisted, }; static const cy_app_desc_t eth_app_desc = {.id = 7, @@ -122,6 +134,24 @@ static const cy_app_desc_t eth_app_desc = {.id = 7, * STATIC FUNCTIONS *****************************************************************************/ +static bool is_token_whitelisted(const uint8_t *address, + const erc20_contracts_t **contract) { + const erc20_contracts_t *match = NULL; + bool status = false; + for (int16_t i = 0; i < ETH_WHITELISTED_CONTRACTS_COUNT; i++) { + if (memcmp(address, eth_contracts[i].address, EVM_ADDRESS_LENGTH) == 0) { + match = ð_contracts[i]; + status = true; + break; + } + } + + if (NULL != contract) { + *contract = match; + } + return status; +} + /***************************************************************************** * GLOBAL FUNCTIONS *****************************************************************************/ diff --git a/apps/evm_family/evm_context.h b/apps/evm_family/evm_context.h index e304cbf4..8f347ba8 100644 --- a/apps/evm_family/evm_context.h +++ b/apps/evm_family/evm_context.h @@ -44,8 +44,8 @@ typedef struct { * https://github.com/ethereum/EIPs/blob/830708a049fc982fd595cb0c4dca703aebefd003/EIPS/eip-2294.md */ const uint64_t chain_id; - const erc20_contracts_t *whitelisted_contracts; - const uint16_t whitelist_count; + bool (*is_token_whitelisted)(const uint8_t *address, + const erc20_contracts_t **contract); } evm_config_t; /***************************************************************************** diff --git a/apps/evm_family/evm_contracts.c b/apps/evm_family/evm_contracts.c new file mode 100644 index 00000000..43110524 --- /dev/null +++ b/apps/evm_family/evm_contracts.c @@ -0,0 +1,362 @@ +/** + * @file evm_contract_data_helper.c + * @author Cypherock X1 Team + * @brief Helper functions for interpreting contract data in EVM transactions + * @copyright Copyright (c) 2023 HODL TECH PTE LTD + *
You may obtain a copy of license at https://mitcc.org/ + * + ****************************************************************************** + * @attention + * + * (c) Copyright 2023 by HODL TECH PTE LTD + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + * "Commons Clause" License Condition v1.0 + * + * The Software is provided to you by the Licensor under the License, + * as defined below, subject to the following condition. + * + * Without limiting other conditions in the License, the grant of + * rights under the License will not include, and the License does not + * grant to you, the right to Sell the Software. + * + * For purposes of the foregoing, "Sell" means practicing any or all + * of the rights granted to you under the License to provide to third + * parties, for a fee or other consideration (including without + * limitation fees for hosting or consulting/ support services related + * to the Software), a product or service whose value derives, entirely + * or substantially, from the functionality of the Software. Any license + * notice or attribution required by the License must also include + * this Commons Clause License Condition notice. + * + * Software: All X1Wallet associated files. + * License: MIT + * Licensor: HODL TECH PTE LTD + * + ****************************************************************************** + */ + +/***************************************************************************** + * INCLUDES + *****************************************************************************/ + +#include "evm_contracts.h" + +#include "abi.h" + +/***************************************************************************** + * EXTERN VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE MACROS AND DEFINES + *****************************************************************************/ + +/***************************************************************************** + * PRIVATE TYPEDEFS + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTION PROTOTYPES + *****************************************************************************/ + +/** + * @brief This function checks if an EVM function tag is supported by the + * X1 wallet parser. If a known function is found, a UI element of type + * ui_display_node is created. Also, dpAbiTypeArray is updated to point + * the argument type list for that function. + * + * @param functionTag The function tag found in the EVM transaction payload + * @param dpAbiTypeArray Pointer to start of the argument type array for the + * identified function. + * @param displayNode Pointer to storage for ui_display_node + * @return uint8_t The number of arguments in an identified function. + */ +static uint8_t ETH_DetectFunction(const uint32_t functionTag, + Abi_Type_e const **const dpAbiTypeArray, + ui_display_node **displayNode); + +/***************************************************************************** + * STATIC VARIABLES + *****************************************************************************/ + +static const Abi_Type_e EVM_swapDataType[EVM_swap_NUM_ARGS] = { + Abi_address_e, + Abi_address_e, + Abi_address_e, + Abi_address_e, + Abi_address_e, + Abi_uint256_e, + Abi_uint256_e, + Abi_uint256_e, + Abi_bytes_dynamic_e, + Abi_bytes_dynamic_e, +}; + +static const char *EVM_swap_Title = "Function: swap"; +static const char *EVM_swap_Signature = + "swap(address,(address,address,address,address,uint256,uint256,uint256)," + "bytes,bytes)"; + +static const Abi_Type_e EVM_uniswapV3SwapDataType[EVM_uniswapV3Swap_NUM_ARGS] = + {Abi_uint256_e, Abi_uint256_e, Abi_uint256_array_dynamic_e}; + +static const char *EVM_uniswapV3Swap_Title = "Function: uniswapV3Swap"; +static const char *EVM_uniswapV3Swap_Signature = + "uniswapV3Swap(uint256,uint256,uint256[])"; + +static const Abi_Type_e + EVM_safeTransferFromDataType[EVM_safeTransferFrom_NUM_ARGS] = { + Abi_address_e, + Abi_address_e, + Abi_uint256_e, +}; + +static const char *EVM_safeTransferFrom_Title = "Function: safeTransferFrom"; +static const char *EVM_safeTransferFrom_Signature = + "safeTransferFrom(address,address,uint256)"; + +static const Abi_Type_e EVM_depositDataType[EVM_deposit_NUM_ARGS] = {}; + +static const char *EVM_deposit_Title = "Function: deposit"; +static const char *EVM_deposit_Signature = "deposit()"; + +static const Abi_Type_e EVM_transferDataType[EVM_transfer_NUM_ARGS] = { + Abi_address_e, + Abi_uint256_e, +}; +static const char *EVM_transfer_Title = "Function: transfer"; +static const char *EVM_transfer_Signature = "transfer(address,uint256)"; + +/***************************************************************************** + * GLOBAL VARIABLES + *****************************************************************************/ + +/***************************************************************************** + * STATIC FUNCTIONS + *****************************************************************************/ + +static uint8_t ETH_DetectFunction(const uint32_t functionTag, + Abi_Type_e const **const dpAbiTypeArray, + ui_display_node **displayNode) { + if (NULL == dpAbiTypeArray) { + return 0; + } + + uint8_t numArgsInFunction = 0; + const char *EvmFunctionTitle = NULL; + const char *EvmFunctionSignature = NULL; + + switch (functionTag) { + case EVM_swap_TAG: { + numArgsInFunction = EVM_swap_NUM_ARGS; + *(dpAbiTypeArray) = &(EVM_swapDataType[0]); + EvmFunctionTitle = EVM_swap_Title; + EvmFunctionSignature = EVM_swap_Signature; + break; + } + case EVM_uniswapV3Swap_TAG: { + numArgsInFunction = EVM_uniswapV3Swap_NUM_ARGS; + *(dpAbiTypeArray) = &(EVM_uniswapV3SwapDataType[0]); + EvmFunctionTitle = EVM_uniswapV3Swap_Title; + EvmFunctionSignature = EVM_uniswapV3Swap_Signature; + break; + } + case EVM_safeTransferFrom_TAG: { + numArgsInFunction = EVM_safeTransferFrom_NUM_ARGS; + *(dpAbiTypeArray) = &(EVM_safeTransferFromDataType[0]); + EvmFunctionTitle = EVM_safeTransferFrom_Title; + EvmFunctionSignature = EVM_safeTransferFrom_Signature; + break; + } + case EVM_deposit_TAG: { + numArgsInFunction = EVM_deposit_NUM_ARGS; + *(dpAbiTypeArray) = &(EVM_depositDataType[0]); + EvmFunctionTitle = EVM_deposit_Title; + EvmFunctionSignature = EVM_deposit_Signature; + break; + } + case EVM_transfer_TAG: { + numArgsInFunction = EVM_transfer_NUM_ARGS; + *(dpAbiTypeArray) = &(EVM_transferDataType[0]); + EvmFunctionTitle = EVM_transfer_Title; + EvmFunctionSignature = EVM_transfer_Signature; + break; + } + default: { + break; + } + } + + /* Add the detected function as part of verification in the UI */ + if ((NULL != EvmFunctionTitle) && (NULL != EvmFunctionSignature)) { + ui_display_node *pAbiDispNode; + pAbiDispNode = ui_create_display_node(EvmFunctionTitle, + strnlen(EvmFunctionTitle, 100), + EvmFunctionSignature, + strnlen(EvmFunctionSignature, 100)); + + if (*displayNode == NULL) { + *displayNode = pAbiDispNode; + } else { + ui_display_node *temp = *displayNode; + while (temp->next != NULL) { + temp = temp->next; + } + temp->next = pAbiDispNode; + } + } + + return numArgsInFunction; +} + +/***************************************************************************** + * GLOBAL FUNCTIONS + *****************************************************************************/ + +uint8_t ETH_ExtractArguments(const uint8_t *pAbiPayload, + const uint64_t sizeOfPayload, + ui_display_node **displayNode) { + uint8_t returnCode = ETH_BAD_ARGUMENTS; + + /* Size of transaction payload must be atleast EVM_FUNC_SIGNATURE_LENGTH */ + if ((NULL == pAbiPayload) || (EVM_FUNC_SIGNATURE_LENGTH > sizeOfPayload) || + (NULL == displayNode)) { + return returnCode; + } + + uint8_t *pCurrHeadPtr = (uint8_t *)pAbiPayload; + + /** + * Detect if the ethereum unsigned txn payload includes a function that + * we can decode + * pArgumentAbiType will hold pointer to array with information regarding + * the types of argument corresponding to a function signature + */ + uint32_t functionTag = U32_READ_BE_ARRAY(pCurrHeadPtr); + uint8_t numArgsInFunction = 0; + Abi_Type_e const *pArgumentAbiType = NULL; + + numArgsInFunction = + ETH_DetectFunction(functionTag, &pArgumentAbiType, displayNode); + + /** + * If pArgumentAbiType is NULL, that means ETH_DetectFunction did not + * detect a supported function + * Therefore we should return from here + */ + if (NULL == pArgumentAbiType) { + returnCode = ETH_UTXN_FUNCTION_NOT_FOUND; + return returnCode; + } + + /* Increment pCurrHeadPtr to point to first argument */ + pCurrHeadPtr += EVM_FUNC_SIGNATURE_LENGTH; + + /** + * Save the base address of the first argument; it will be required in case + * of any dynamic element encoded in ABI format as the offset is calculated + * from the base of the first argument. + */ + const uint8_t *pPayloadBasePtr = pCurrHeadPtr; + uint8_t currArgument; + + for (currArgument = 0; currArgument < numArgsInFunction; currArgument++) { + /* Ensure that we are reading from within the bounds */ + if (UTIL_IN_BOUNDS != UTIL_CheckBound(pAbiPayload, + sizeOfPayload, + pCurrHeadPtr, + ABI_ELEMENT_SZ_IN_BYTES)) { + returnCode = ETH_UTXN_BAD_PAYLOAD; + break; + } + + ui_display_node *pAbiDispNode = NULL; + + /* Check if we are reading a dynamic or static element */ + if (Abi_bytes_dynamic_e <= pArgumentAbiType[currArgument]) { + uint8_t *pDynamicDataPtr = NULL; + uint8_t abiReturnCode = ABI_PROCESS_INCOMPLETE; + uint32_t numBytesReturned = 0; + + /* Get the information regarding dynamic data types */ + abiReturnCode = + Abi_DynamicHelp(pArgumentAbiType[currArgument], + pCurrHeadPtr, + pPayloadBasePtr, + (sizeOfPayload - EVM_FUNC_SIGNATURE_LENGTH), + &numBytesReturned, + &pDynamicDataPtr); + + /** + * If abiReturnCode is not ABI_PROCESS_COMPLETE, that means + * the function spotted an invalid argument during the call, or + * the payload was not good as bounds check failed internally + */ + if ((ABI_PROCESS_COMPLETE != abiReturnCode) || + (NULL == pDynamicDataPtr)) { + returnCode = ETH_UTXN_BAD_PAYLOAD; + break; + } + + /** + * Handle stringify based on dynamic data type + * Abi_bytes_dynamic_e can be handled directly using number of bytes + * Abi_uint256_array_dynamic_e needs to be handled in a loop, for each + * uint256 bit data + */ + if (Abi_bytes_dynamic_e == pArgumentAbiType[currArgument]) { + pAbiDispNode = + ABI_Stringify(Abi_bytes_e, pDynamicDataPtr, numBytesReturned); + } else if (Abi_uint256_array_dynamic_e == + pArgumentAbiType[currArgument]) { + uint32_t item; + for (item = 0; item < numBytesReturned; item++) { + uint8_t *pStaticData = + (uint8_t *)(pDynamicDataPtr + (ABI_ELEMENT_SZ_IN_BYTES * item)); + + pAbiDispNode = ABI_Stringify(Abi_uint256_e, pStaticData, 0); + } + } + } else /* Static elements can be stringified straight away */ + { + pAbiDispNode = + ABI_Stringify(pArgumentAbiType[currArgument], pCurrHeadPtr, 0); + } + + pCurrHeadPtr += ABI_ELEMENT_SZ_IN_BYTES; + returnCode = ETH_UTXN_ABI_DECODE_OK; + + if (*displayNode == NULL) { + *displayNode = pAbiDispNode; + } else { + ui_display_node *temp = *displayNode; + while (temp->next != NULL) { + temp = temp->next; + } + temp->next = pAbiDispNode; + } + } + + return returnCode; +} diff --git a/apps/evm_family/evm_contracts.h b/apps/evm_family/evm_contracts.h index e6f39b2b..e88c26b7 100644 --- a/apps/evm_family/evm_contracts.h +++ b/apps/evm_family/evm_contracts.h @@ -15,6 +15,8 @@ #include +#include "coin_utils.h" + /***************************************************************************** * MACROS AND DEFINES *****************************************************************************/ @@ -25,12 +27,29 @@ /// EVM function parameter block-size length #define EVM_FUNC_SIGNATURE_LENGTH 4 -/// EVM transfer method signature -#define TRANSFER_FUNC_SIGNATURE 0xa9059cbb - /// Length of Ethereum public addresses in bytes #define EVM_ADDRESS_LENGTH 20 +/** Refer https://www.4byte.directory/signatures/?bytes4_signature=0x7c025200 */ +#define EVM_swap_TAG (0x12aa3caf) +#define EVM_swap_NUM_ARGS 10 + +/** Refer https://www.4byte.directory/signatures/?bytes4_signature=0xe449022e */ +#define EVM_uniswapV3Swap_TAG (0xe449022e) +#define EVM_uniswapV3Swap_NUM_ARGS 3 + +/** Refer https://www.4byte.directory/signatures/?bytes4_signature=0x42842e0e */ +#define EVM_safeTransferFrom_TAG (0x42842e0e) +#define EVM_safeTransferFrom_NUM_ARGS 3 + +/** Refer https://www.4byte.directory/signatures/?bytes4_signature=0xd0e30db0 */ +#define EVM_deposit_TAG (0xd0e30db0) +#define EVM_deposit_NUM_ARGS 0 + +/** Refer https://www.4byte.directory/signatures/?bytes4_signature=0xa9059cbb */ +#define EVM_transfer_TAG (0xa9059cbb) +#define EVM_transfer_NUM_ARGS 2 + /** * @brief An expected limit on length of Ethereum based ERC20 token symbols. * @details The token symbol is also exchanged in txn_metadata.token_name. @@ -40,6 +59,11 @@ */ #define ETHEREUM_TOKEN_SYMBOL_LENGTH 20 +#define ETH_UTXN_ABI_DECODE_OK (0xAA) +#define ETH_UTXN_BAD_PAYLOAD (0x11) +#define ETH_UTXN_FUNCTION_NOT_FOUND (0x11) +#define ETH_BAD_ARGUMENTS (0x22) + /***************************************************************************** * TYPEDEFS *****************************************************************************/ @@ -72,4 +96,22 @@ typedef struct erc20_contracts { * GLOBAL FUNCTION PROTOTYPES *****************************************************************************/ +/** + * @brief This function extracts Abi encoded arguments for EVM functions into UI + * compatible nodes ui_display_node(s) + * + * @param pAbiPayload Pointer to start of payload of the EVM transaction + * @param sizeOfUTxn Size of payload of the EVM transaction + * @param displayNode Pointer to storage for ui_display_node + * @return uint8_t Depicts the status of operation for this function + * @retval ETH_BAD_ARGUMENTS: If any argument is invalid + * @retval ETH_UTXN_FUNCTION_NOT_FOUND: If a function NOT supported by X1 wallet + * is in the EVM tx + * @retval ETH_UTXN_BAD_PAYLOAD: If a payload contains invalid data + * @retval ETH_UTXN_ABI_DECODE_OK: If the arguments are extracted successfully + */ +uint8_t ETH_ExtractArguments(const uint8_t *pAbiPayload, + const uint64_t sizeOfPayload, + ui_display_node **displayNode); + #endif // EVM_CONTRACTS_H diff --git a/apps/evm_family/evm_sign_txn.c b/apps/evm_family/evm_sign_txn.c index b2174ab5..79b908c8 100644 --- a/apps/evm_family/evm_sign_txn.c +++ b/apps/evm_family/evm_sign_txn.c @@ -75,9 +75,6 @@ * EXTERN VARIABLES *****************************************************************************/ -extern bool evm_is_token_whitelisted; -extern ui_display_node *current_display_node; - /***************************************************************************** * PRIVATE MACROS AND DEFINES *****************************************************************************/ @@ -268,12 +265,8 @@ STATIC bool handle_initiate_query(const evm_query_t *query) { } // TODO: handle prompts for different transaction types - snprintf(msg, - sizeof(msg), - UI_TEXT_SEND_PROMPT, - g_evm_app->lunit_name, - g_evm_app->name, - wallet_name); + snprintf( + msg, sizeof(msg), UI_TEXT_SIGN_TXN_PROMPT, g_evm_app->name, wallet_name); // Take user consent to sign the transaction for the wallet if (!core_confirmation(msg, evm_send_error)) { return false; @@ -333,9 +326,12 @@ STATIC bool fetch_valid_transaction(evm_query_t *query) { return status; } // decode and verify the received transaction - if (0 != evm_byte_array_to_unsigned_txn( + if (0 != evm_decode_unsigned_txn( txn_context->transaction, total_size, txn_context) || + EVM_TXN_INVALID_DATA == txn_context->txn_type || !evm_validate_unsigned_txn(txn_context)) { + evm_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, + ERROR_DATA_FLOW_INVALID_DATA); return status; } @@ -345,29 +341,22 @@ STATIC bool fetch_valid_transaction(evm_query_t *query) { STATIC bool get_user_verification() { bool status = false; - switch (txn_context->transaction_info.payload_status) { - case PAYLOAD_ABSENT: + switch (txn_context->txn_type) { + case EVM_TXN_NO_DATA: + case EVM_TXN_TRANSFER_FUNC: status = evm_verify_transfer(txn_context); break; - case PAYLOAD_WHITELISTED: - if (evm_is_token_whitelisted) { - status = evm_verify_transfer(txn_context); - } else { - status = evm_verify_clear_signing(txn_context); - } + case EVM_TXN_KNOWN_FUNC_SIG: + status = evm_verify_clear_signing(txn_context); break; - case PAYLOAD_SIGNATURE_NOT_WHITELISTED: + case EVM_TXN_UNKNOWN_FUNC_SIG: // TODO: link with wallet setting status = evm_verify_blind_signing(txn_context); break; - case PAYLOAD_CONTRACT_NOT_WHITELISTED: - status = evm_verify_clear_signing(txn_context); - break; - - case PAYLOAD_CONTRACT_INVALID: + case EVM_TXN_INVALID_DATA: default: // cannot reach; should be caught already break; @@ -435,7 +424,6 @@ void evm_sign_transaction(evm_query_t *query) { txn_context = (evm_txn_context_t *)malloc(sizeof(evm_txn_context_t)); memzero(txn_context, sizeof(evm_txn_context_t)); evm_sign_txn_signature_response_t sig = {0}; - current_display_node = NULL; if (handle_initiate_query(query) && fetch_valid_transaction(query) && get_user_verification() && sign_transaction(&sig) && diff --git a/apps/evm_family/evm_txn_helpers.c b/apps/evm_family/evm_txn_helpers.c index 4b71aee4..99ba7976 100644 --- a/apps/evm_family/evm_txn_helpers.c +++ b/apps/evm_family/evm_txn_helpers.c @@ -100,17 +100,13 @@ static uint64_t get_decode_length(const uint8_t *seq, seq_type *type); /** - * @brief The function identifies the payload type in the provided transaction + * @brief * - * @param eth_utxn_ptr Reference to const instance of evm_unsigned_txn - * @param metadata_ptr Reference to txn_metadata - * @return PAYLOAD_STATUS indicating the identified category of payload in the - * transaction + * @param txn_context + * @return EVM_TRANSACTION_TYPE */ -static PAYLOAD_STATUS evm_decode_txn_payload( - const evm_sign_txn_initiate_request_t *init_info, - const evm_unsigned_txn *utxn_ptr, - const erc20_contracts_t **contract); +static EVM_TRANSACTION_TYPE evm_decode_transaction_type( + evm_txn_context_t *txn_context); /***************************************************************************** * STATIC VARIABLES @@ -120,8 +116,6 @@ static PAYLOAD_STATUS evm_decode_txn_payload( * GLOBAL VARIABLES *****************************************************************************/ -bool evm_is_token_whitelisted = false; - /***************************************************************************** * STATIC FUNCTIONS *****************************************************************************/ @@ -174,56 +168,49 @@ static uint64_t get_decode_length(const uint8_t *seq, return item_bytes_len; } -static PAYLOAD_STATUS evm_decode_txn_payload( - const evm_sign_txn_initiate_request_t *init_info, - const evm_unsigned_txn *utxn_ptr, - const erc20_contracts_t **contract) { - PAYLOAD_STATUS result = PAYLOAD_ABSENT; - evm_is_token_whitelisted = false; - - if (utxn_ptr->payload_size > 0) { - if ((utxn_ptr->payload_size >= 4) && - (U32_READ_BE_ARRAY(utxn_ptr->payload) == TRANSFER_FUNC_SIGNATURE) && - (init_info->is_token_transfer) && - (init_info->chain_id == ETHEREUM_MAINNET_CHAIN)) { - for (int16_t i = 0; i < g_evm_app->whitelist_count; i++) { - if (strncmp(init_info->token_symbol, - g_evm_app->whitelisted_contracts[i].symbol, - ETHEREUM_TOKEN_SYMBOL_LENGTH) == 0) { - evm_is_token_whitelisted = true; - result = (memcmp(utxn_ptr->to_address, - g_evm_app->whitelisted_contracts[i].address, - EVM_ADDRESS_LENGTH) == 0) - ? PAYLOAD_WHITELISTED - : PAYLOAD_CONTRACT_INVALID; - - if (PAYLOAD_WHITELISTED == result) { - *contract = &g_evm_app->whitelisted_contracts[i]; - } - break; - } - } - } - if (!evm_is_token_whitelisted) { - // TODO: detecting clear sign or blind sign transaction using the function - // tag value along with function signature - result = - (ETH_ExtractArguments(utxn_ptr->payload, utxn_ptr->payload_size) == - ETH_UTXN_ABI_DECODE_OK) - ? PAYLOAD_WHITELISTED - : PAYLOAD_SIGNATURE_NOT_WHITELISTED; +static EVM_TRANSACTION_TYPE evm_decode_transaction_type( + evm_txn_context_t *txn_context) { + if (0 == txn_context->transaction_info.data_size) { + return EVM_TXN_NO_DATA; + } + + if (EVM_FUNC_SIGNATURE_LENGTH > txn_context->transaction_info.data_size) { + // function signature should be of 4-bytes + return EVM_TXN_INVALID_DATA; + } + + uint32_t function_tag = U32_READ_BE_ARRAY(txn_context->transaction_info.data); + if (EVM_transfer_TAG == function_tag && + g_evm_app->is_token_whitelisted(txn_context->transaction_info.to_address, + &txn_context->contract)) { + return EVM_TXN_TRANSFER_FUNC; + } + + if (EVM_swap_TAG == function_tag || EVM_uniswapV3Swap_TAG == function_tag || + EVM_safeTransferFrom_TAG == function_tag || + EVM_deposit_TAG == function_tag || EVM_transfer_TAG == function_tag) { + // decode the contract data for display + if (ETH_UTXN_ABI_DECODE_OK != + ETH_ExtractArguments(txn_context->transaction_info.data, + txn_context->transaction_info.data_size, + &txn_context->display_node)) { + return EVM_TXN_INVALID_DATA; + } else { + return EVM_TXN_KNOWN_FUNC_SIG; } } - return result; + + // unidentified function signature + return EVM_TXN_UNKNOWN_FUNC_SIG; } /***************************************************************************** * GLOBAL FUNCTIONS *****************************************************************************/ -int evm_byte_array_to_unsigned_txn(const uint8_t *evm_utxn_byte_array, - size_t byte_array_len, - evm_txn_context_t *txn_context) { +int evm_decode_unsigned_txn(const uint8_t *evm_utxn_byte_array, + size_t byte_array_len, + evm_txn_context_t *txn_context) { if (evm_utxn_byte_array == NULL || txn_context == NULL) { return -1; } @@ -325,11 +312,9 @@ int evm_byte_array_to_unsigned_txn(const uint8_t *evm_utxn_byte_array, offset += decoded_len; if (type != STRING) return -1; - utxn_ptr->payload_size = item_bytes_len; - utxn_ptr->payload = &evm_utxn_byte_array[offset]; + utxn_ptr->data_size = item_bytes_len; + utxn_ptr->data = &evm_utxn_byte_array[offset]; offset += (int64_t)item_bytes_len; - utxn_ptr->payload_status = evm_decode_txn_payload( - &txn_context->init_info, utxn_ptr, &txn_context->contract); // chain id item_bytes_len = get_decode_length(evm_utxn_byte_array + offset, @@ -345,6 +330,7 @@ int evm_byte_array_to_unsigned_txn(const uint8_t *evm_utxn_byte_array, byte_array_len, item_bytes_len, &offset); + txn_context->txn_type = evm_decode_transaction_type(txn_context); return (offset > 0 ? 0 : -1); } @@ -361,26 +347,35 @@ bool evm_validate_unsigned_txn(const evm_txn_context_t *txn_context) { // unsigned transaction (cy_read_be(utxn_ptr->chain_id, utxn_ptr->chain_id_size[0]) != g_evm_app->chain_id) || - // Check if token transfer is triggered with whitelisted token and amount - // is non-zero - (txn_context->init_info.is_token_transfer && evm_is_token_whitelisted && + // ensure token transfer is triggered with zero ETH amount + (EVM_TXN_TRANSFER_FUNC == txn_context->txn_type && !is_zero(utxn_ptr->value, utxn_ptr->value_size[0])) || + // Check if token transfer is triggered with whitelisted token; ensure + // reference is valid + (EVM_TXN_TRANSFER_FUNC == txn_context->txn_type && + NULL == txn_context->contract) || // ensure the payload status is valid - (utxn_ptr->payload_status == PAYLOAD_CONTRACT_INVALID)); + (txn_context->txn_type == EVM_TXN_INVALID_DATA)); } -void eth_get_to_address(const evm_unsigned_txn *utxn_ptr, +void eth_get_to_address(const evm_txn_context_t *txn_context, const uint8_t **address) { - if (evm_is_token_whitelisted) { - *address = &utxn_ptr->payload[16]; + const uint8_t *addr = NULL; + if (EVM_TXN_TRANSFER_FUNC == txn_context->txn_type) { + addr = &txn_context->transaction_info.data[16]; } else { - *address = &utxn_ptr->to_address[0]; + addr = &txn_context->transaction_info.to_address[0]; + } + + if (NULL != address) { + *address = addr; } } -uint32_t eth_get_value(const evm_unsigned_txn *utxn_ptr, char *value) { - if (evm_is_token_whitelisted) { - byte_array_to_hex_string(utxn_ptr->payload + EVM_FUNC_SIGNATURE_LENGTH + +uint32_t eth_get_value(const evm_txn_context_t *txn_context, char *value) { + const evm_unsigned_txn *utxn_ptr = &txn_context->transaction_info; + if (EVM_TXN_TRANSFER_FUNC == txn_context->txn_type) { + byte_array_to_hex_string(utxn_ptr->data + EVM_FUNC_SIGNATURE_LENGTH + EVM_FUNC_PARAM_BLOCK_LENGTH, EVM_FUNC_PARAM_BLOCK_LENGTH, value, @@ -427,23 +422,15 @@ void eth_get_fee_string(const evm_unsigned_txn *utxn_ptr, } uint8_t evm_get_decimal(const evm_txn_context_t *txn_context) { - // TODO: in case - return (PAYLOAD_WHITELISTED == txn_context->transaction_info.payload_status && - NULL != txn_context->contract) - ? txn_context->contract->decimal - : ETH_DECIMAL; + if (EVM_TXN_TRANSFER_FUNC == txn_context->txn_type) { + return txn_context->contract->decimal; + } + return ETH_DECIMAL; } const char *evm_get_asset_symbol(const evm_txn_context_t *txn_context) { - if (evm_is_token_whitelisted) - return txn_context->init_info.token_symbol; - else - return g_evm_app->lunit_name; -} - -const char *eth_get_address_title(const evm_unsigned_txn *utxn_ptr) { - return ( - (utxn_ptr->payload_status != PAYLOAD_ABSENT && !evm_is_token_whitelisted) - ? ui_text_verify_contract - : ui_text_verify_address); + if (EVM_TXN_TRANSFER_FUNC == txn_context->txn_type) { + return txn_context->contract->symbol; + } + return g_evm_app->lunit_name; } diff --git a/apps/evm_family/evm_txn_helpers.h b/apps/evm_family/evm_txn_helpers.h index d96cd93f..5f1d4644 100644 --- a/apps/evm_family/evm_txn_helpers.h +++ b/apps/evm_family/evm_txn_helpers.h @@ -30,23 +30,22 @@ * TYPEDEFS *****************************************************************************/ -/** - * @brief Enum used to represent the status of payload field in a transaction. - * - */ typedef enum { - PAYLOAD_ABSENT = 0x0, // No payload present in the transaction - PAYLOAD_SIGNATURE_NOT_WHITELISTED, // Payload function signature is not - // recognized [Blind Signing] - PAYLOAD_CONTRACT_NOT_WHITELISTED, // [OBSOLETE] Payload function signature - // is whitelisted but contract is not - // (for Transfer function) [Unverified - // Contract] [OBSOLETE] - PAYLOAD_CONTRACT_INVALID, // Payload function signature and contract both - // are whitelisted but doesn't match [Invalid - // Transaction] - PAYLOAD_WHITELISTED, // Payload is completely recognized [Clear Signing] -} PAYLOAD_STATUS; + /// No data present in the transaction + EVM_TXN_NO_DATA = 0x0, + + /// Data present in the transaction is invalid + EVM_TXN_INVALID_DATA, + + /// Function signature in data is not recognized + EVM_TXN_UNKNOWN_FUNC_SIG, + + /// Data is for token transfer function + EVM_TXN_TRANSFER_FUNC, + + /// Function signature is known; decode function params to display + EVM_TXN_KNOWN_FUNC_SIG, +} EVM_TRANSACTION_TYPE; /** * @brief Struct to store Unsigned Ethereum Transaction details. @@ -73,16 +72,14 @@ typedef struct { uint8_t value_size[1]; uint8_t value[ETH_VALUE_SIZE_BYTES]; - uint64_t payload_size; - const uint8_t *payload; + uint64_t data_size; + const uint8_t *data; uint8_t chain_id_size[1]; uint8_t chain_id[8]; uint8_t dummy_r[1]; uint8_t dummy_s[1]; - - PAYLOAD_STATUS payload_status; } evm_unsigned_txn; #pragma pack(pop) @@ -95,10 +92,17 @@ typedef struct { /// remembers the allocated buffer for holding complete unsigned transaction uint8_t *transaction; + /// store for decoded unsigned transaction info evm_unsigned_txn transaction_info; + /// whitelisted contract in the transaction const erc20_contracts_t *contract; + + EVM_TRANSACTION_TYPE txn_type; + + /// pointer to maintain a list of display nodes + ui_display_node *display_node; } evm_txn_context_t; /***************************************************************************** @@ -123,9 +127,9 @@ typedef struct { * @retval 0 Success * @retval -1 Failure */ -int evm_byte_array_to_unsigned_txn(const uint8_t *evm_utxn_byte_array, - size_t byte_array_len, - evm_txn_context_t *txn_context); +int evm_decode_unsigned_txn(const uint8_t *evm_utxn_byte_array, + size_t byte_array_len, + evm_txn_context_t *txn_context); /** * @brief Verifies the unsigned transaction. @@ -151,7 +155,7 @@ bool evm_validate_unsigned_txn(const evm_txn_context_t *txn_context); * @return * @retval */ -void eth_get_to_address(const evm_unsigned_txn *utxn_ptr, +void eth_get_to_address(const evm_txn_context_t *txn_context, const uint8_t **address); /** @@ -165,7 +169,7 @@ void eth_get_to_address(const evm_unsigned_txn *utxn_ptr, * @return * @retval */ -uint32_t eth_get_value(const evm_unsigned_txn *utxn_ptr, char *value); +uint32_t eth_get_value(const evm_txn_context_t *txn_context, char *value); /** * @brief Return the string representation of decimal value of transaction fee @@ -197,13 +201,4 @@ uint8_t evm_get_decimal(const evm_txn_context_t *txn_context); */ const char *evm_get_asset_symbol(const evm_txn_context_t *txn_context); -/** - * @brief Returns the title for address verification in ethereum send flow - * Contract address is verified when sending data with payload except for - * whitelisted tokens - * @param utxn_ptr Pointer to the unsigned transaction for ethereum - * @return const char* - */ -const char *eth_get_address_title(const evm_unsigned_txn *utxn_ptr); - #endif diff --git a/apps/evm_family/evm_user_verification.c b/apps/evm_family/evm_user_verification.c index 6629e91e..01afeaf6 100644 --- a/apps/evm_family/evm_user_verification.c +++ b/apps/evm_family/evm_user_verification.c @@ -73,8 +73,6 @@ * EXTERN VARIABLES *****************************************************************************/ -extern ui_display_node *current_display_node; - /***************************************************************************** * PRIVATE MACROS AND DEFINES *****************************************************************************/ @@ -107,11 +105,10 @@ bool evm_verify_transfer(const evm_txn_context_t *txn_context) { bool status = false; char address[43] = "0x"; const uint8_t *to_address = NULL; - const char *unit = NULL; + const char *unit = evm_get_asset_symbol(txn_context); char value[34] = {'\0'}; char hex_str[30] = {'\0'}; - char display[40] = ""; - const char *title = eth_get_address_title(&txn_context->transaction_info); + char display[80] = ""; // TODO: verify transaction nonce; this is pending on settings option #if 0 @@ -122,22 +119,24 @@ bool evm_verify_transfer(const evm_txn_context_t *txn_context) { #endif // verify recipient address; TODO: handle harmony address encoding - eth_get_to_address(&txn_context->transaction_info, &to_address); + eth_get_to_address(txn_context, &to_address); ethereum_address_checksum( to_address, &address[2], false, g_evm_app->chain_id); - if (!core_scroll_page(title, address, evm_send_error)) { + snprintf( + display, sizeof(display), UI_TEXT_BTC_SEND_PROMPT, unit, g_evm_app->name); + if (!core_scroll_page(NULL, display, evm_send_error) || + !core_scroll_page(ui_text_verify_address, address, evm_send_error)) { return status; } // verify recipient amount - uint8_t len = eth_get_value(&txn_context->transaction_info, hex_str); + uint8_t len = eth_get_value(txn_context, hex_str); if (!convert_byte_array_to_decimal_string( len, evm_get_decimal(txn_context), hex_str, value, sizeof(value))) { evm_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 1); return status; } - unit = evm_get_asset_symbol(txn_context); snprintf(display, sizeof(display), UI_TEXT_VERIFY_AMOUNT, value, unit); if (!core_confirmation(display, evm_send_error)) { return status; @@ -180,14 +179,19 @@ bool evm_verify_clear_signing(const evm_txn_context_t *txn_context) { return false; } - while (NULL != current_display_node) { - if (!core_scroll_page(current_display_node->title, - current_display_node->value, - evm_send_error)) { + ui_display_node *ui_node = txn_context->display_node; + while (NULL != ui_node) { + if (!core_scroll_page(ui_node->title, ui_node->value, evm_send_error)) { return false; } - current_display_node = current_display_node->next; + ui_node = ui_node->next; } + /** + * The function ETH_ExtractArguments prepares the list of display nodes + * containing presentable strings. The function reserves heap memory for the + * list elements. This is the earliest safe point to free the memory. + */ + cy_free(); return true; } diff --git a/apps/evm_family/fantom/fantom_app.c b/apps/evm_family/fantom/fantom_app.c index 8126fc07..8ee16dc8 100644 --- a/apps/evm_family/fantom/fantom_app.c +++ b/apps/evm_family/fantom/fantom_app.c @@ -92,6 +92,20 @@ extern const erc20_contracts_t fantom_contracts[]; * STATIC FUNCTION PROTOTYPES *****************************************************************************/ +/** + * @brief Checks if the provided token address is whitelisted and return the + * matching contract instance. + * + * @param address Reference to the buffer containing the token address + * @param contract Pointer to store the matched contract address instance + * + * @return bool Indicating if the provided token address is whitelisted + * @return true If the address matches to an entry in the whitelist + * @return false If the address does not match to an entry in the whitelist + */ +static bool is_token_whitelisted(const uint8_t *address, + const erc20_contracts_t **contract); + /***************************************************************************** * STATIC VARIABLES *****************************************************************************/ @@ -102,8 +116,7 @@ static const evm_config_t fantom_app_config = { .chain_id = 250, // whitelisted contracts - .whitelisted_contracts = NULL, - .whitelist_count = FANTOM_WHITELISTED_CONTRACTS_COUNT, + .is_token_whitelisted = is_token_whitelisted, }; static const cy_app_desc_t fantom_app_desc = {.id = 12, @@ -124,6 +137,14 @@ static const cy_app_desc_t fantom_app_desc = {.id = 12, * STATIC FUNCTIONS *****************************************************************************/ +static bool is_token_whitelisted(const uint8_t *address, + const erc20_contracts_t **contract) { + if (NULL != contract) { + *contract = NULL; + } + return false; +} + /***************************************************************************** * GLOBAL FUNCTIONS *****************************************************************************/ diff --git a/apps/evm_family/optimism/optimism_app.c b/apps/evm_family/optimism/optimism_app.c index 02d875ed..372de315 100644 --- a/apps/evm_family/optimism/optimism_app.c +++ b/apps/evm_family/optimism/optimism_app.c @@ -92,6 +92,20 @@ extern const erc20_contracts_t optimism_contracts[]; * STATIC FUNCTION PROTOTYPES *****************************************************************************/ +/** + * @brief Checks if the provided token address is whitelisted and return the + * matching contract instance. + * + * @param address Reference to the buffer containing the token address + * @param contract Pointer to store the matched contract address instance + * + * @return bool Indicating if the provided token address is whitelisted + * @return true If the address matches to an entry in the whitelist + * @return false If the address does not match to an entry in the whitelist + */ +static bool is_token_whitelisted(const uint8_t *address, + const erc20_contracts_t **contract); + /***************************************************************************** * STATIC VARIABLES *****************************************************************************/ @@ -102,8 +116,7 @@ static const evm_config_t optimism_app_config = { .chain_id = 10, // whitelisted contracts - .whitelisted_contracts = NULL, - .whitelist_count = OPTIMISM_WHITELISTED_CONTRACTS_COUNT, + .is_token_whitelisted = is_token_whitelisted, }; static const cy_app_desc_t optimism_app_desc = { @@ -125,6 +138,14 @@ static const cy_app_desc_t optimism_app_desc = { * STATIC FUNCTIONS *****************************************************************************/ +static bool is_token_whitelisted(const uint8_t *address, + const erc20_contracts_t **contract) { + if (NULL != contract) { + *contract = NULL; + } + return false; +} + /***************************************************************************** * GLOBAL FUNCTIONS *****************************************************************************/ diff --git a/apps/evm_family/polygon/polygon_app.c b/apps/evm_family/polygon/polygon_app.c index 25163721..d1b733b0 100644 --- a/apps/evm_family/polygon/polygon_app.c +++ b/apps/evm_family/polygon/polygon_app.c @@ -92,6 +92,20 @@ extern const erc20_contracts_t polygon_contracts[]; * STATIC FUNCTION PROTOTYPES *****************************************************************************/ +/** + * @brief Checks if the provided token address is whitelisted and return the + * matching contract instance. + * + * @param address Reference to the buffer containing the token address + * @param contract Pointer to store the matched contract address instance + * + * @return bool Indicating if the provided token address is whitelisted + * @return true If the address matches to an entry in the whitelist + * @return false If the address does not match to an entry in the whitelist + */ +static bool is_token_whitelisted(const uint8_t *address, + const erc20_contracts_t **contract); + /***************************************************************************** * STATIC VARIABLES *****************************************************************************/ @@ -102,8 +116,7 @@ static const evm_config_t polygon_app_config = { .chain_id = 137, // whitelisted contracts - .whitelisted_contracts = NULL, - .whitelist_count = POLYGON_WHITELISTED_CONTRACTS_COUNT, + .is_token_whitelisted = is_token_whitelisted, }; static const cy_app_desc_t polygon_app_desc = { @@ -125,6 +138,14 @@ static const cy_app_desc_t polygon_app_desc = { * STATIC FUNCTIONS *****************************************************************************/ +static bool is_token_whitelisted(const uint8_t *address, + const erc20_contracts_t **contract) { + if (NULL != contract) { + *contract = NULL; + } + return false; +} + /***************************************************************************** * GLOBAL FUNCTIONS *****************************************************************************/ diff --git a/common/coin_support/eth.c b/common/coin_support/eth.c index a86671ff..d1e4a8f5 100644 --- a/common/coin_support/eth.c +++ b/common/coin_support/eth.c @@ -72,65 +72,6 @@ static uint8_t rlp_encode_decimal(uint64_t dec, uint8_t offset, uint8_t *metadata); -/* Refer https://www.4byte.directory/signatures/?bytes4_signature=0x7c025200 */ -#define EVM_swap_TAG (0x12aa3caf) -#define EVM_swap_NUM_ARGS 10 -const Abi_Type_e EVM_swapDataType[EVM_swap_NUM_ARGS] = {Abi_address_e, - Abi_address_e, - Abi_address_e, - Abi_address_e, - Abi_address_e, - Abi_uint256_e, - Abi_uint256_e, - Abi_uint256_e, - Abi_bytes_dynamic_e, - Abi_bytes_dynamic_e}; - -const char *EVM_swap_Title = "Function: swap"; -const char *EVM_swap_Signature = - "swap(address,(address,address,address,address,uint256,uint256,uint256)," - "bytes,bytes)"; - -/* Refer https://www.4byte.directory/signatures/?bytes4_signature=0xe449022e */ -#define EVM_uniswapV3Swap_TAG (0xe449022e) -#define EVM_uniswapV3Swap_NUM_ARGS 3 -const Abi_Type_e EVM_uniswapV3SwapDataType[EVM_uniswapV3Swap_NUM_ARGS] = { - Abi_uint256_e, - Abi_uint256_e, - Abi_uint256_array_dynamic_e}; - -const char *EVM_uniswapV3Swap_Title = "Function: uniswapV3Swap"; -const char *EVM_uniswapV3Swap_Signature = - "uniswapV3Swap(uint256,uint256,uint256[])"; - -/* Refer https://www.4byte.directory/signatures/?bytes4_signature=0x42842e0e */ -#define EVM_safeTransferFrom_TAG (0x42842e0e) -#define EVM_safeTransferFrom_NUM_ARGS 3 -const Abi_Type_e EVM_safeTransferFromDataType[EVM_safeTransferFrom_NUM_ARGS] = { - Abi_address_e, - Abi_address_e, - Abi_uint256_e}; - -const char *EVM_safeTransferFrom_Title = "Function: safeTransferFrom"; -const char *EVM_safeTransferFrom_Signature = - "safeTransferFrom(address,address,uint256)"; - -/* Refer https://www.4byte.directory/signatures/?bytes4_signature=0xd0e30db0 */ -#define EVM_deposit_TAG (0xd0e30db0) -#define EVM_deposit_NUM_ARGS 0 -const Abi_Type_e EVM_depositDataType[EVM_deposit_NUM_ARGS] = {}; - -const char *EVM_deposit_Title = "Function: deposit"; -const char *EVM_deposit_Signature = "deposit()"; - -/* Refer https://www.4byte.directory/signatures/?bytes4_signature=0xa9059cbb */ -#define EVM_transfer_TAG (0xa9059cbb) -#define EVM_transfer_NUM_ARGS 2 -const Abi_Type_e EVM_transferDataType[EVM_transfer_NUM_ARGS] = {Abi_address_e, - Abi_uint256_e}; -const char *EVM_transfer_Title = "Function: transfer"; -const char *EVM_transfer_Signature = "transfer(address,uint256)"; - extern bool evm_is_token_whitelisted; /** @@ -313,213 +254,3 @@ void sig_unsigned_byte_array(const uint8_t *eth_unsigned_txn_byte_array, ecdsa_sign_digest(&secp256k1, hdnode.private_key, digest, sig, &recid, NULL); memcpy(sig + 64, &recid, 1); } - -/** - * @brief This function checks if an EVM function tag is supported by the - * X1 wallet parser. If a known function is found, a UI element of type - * ui_display_node is created. Also, dpAbiTypeArray is updated to point - * the argument type list for that function. - * - * @param functionTag The function tag found in the EVM transaction payload - * @param dpAbiTypeArray Pointer to start of the argument type array for the - * identified function. - * @return uint8_t The number of arguments in an identified function. - */ -static uint8_t ETH_DetectFunction(const uint32_t functionTag, - Abi_Type_e const **const dpAbiTypeArray) { - if (NULL == dpAbiTypeArray) { - return 0; - } - - uint8_t numArgsInFunction = 0; - const char *EvmFunctionTitle = NULL; - const char *EvmFunctionSignature = NULL; - - switch (functionTag) { - case EVM_swap_TAG: { - numArgsInFunction = EVM_swap_NUM_ARGS; - *(dpAbiTypeArray) = (Abi_Type_e *)(&(EVM_swapDataType[0])); - EvmFunctionTitle = EVM_swap_Title; - EvmFunctionSignature = EVM_swap_Signature; - break; - } - case EVM_uniswapV3Swap_TAG: { - numArgsInFunction = EVM_uniswapV3Swap_NUM_ARGS; - *(dpAbiTypeArray) = (Abi_Type_e *)(&(EVM_uniswapV3SwapDataType[0])); - EvmFunctionTitle = EVM_uniswapV3Swap_Title; - EvmFunctionSignature = EVM_uniswapV3Swap_Signature; - break; - } - case EVM_safeTransferFrom_TAG: { - numArgsInFunction = EVM_safeTransferFrom_NUM_ARGS; - *(dpAbiTypeArray) = (Abi_Type_e *)(&(EVM_safeTransferFromDataType[0])); - EvmFunctionTitle = EVM_safeTransferFrom_Title; - EvmFunctionSignature = EVM_safeTransferFrom_Signature; - break; - } - case EVM_deposit_TAG: { - numArgsInFunction = EVM_deposit_NUM_ARGS; - *(dpAbiTypeArray) = (Abi_Type_e *)(&(EVM_depositDataType[0])); - EvmFunctionTitle = EVM_deposit_Title; - EvmFunctionSignature = EVM_deposit_Signature; - break; - } - case EVM_transfer_TAG: { - numArgsInFunction = EVM_transfer_NUM_ARGS; - *(dpAbiTypeArray) = (Abi_Type_e *)(&(EVM_transferDataType[0])); - EvmFunctionTitle = EVM_transfer_Title; - EvmFunctionSignature = EVM_transfer_Signature; - break; - } - default: { - break; - } - } - - /* Add the detected function as part of verification in the UI */ - if ((NULL != EvmFunctionTitle) && (NULL != EvmFunctionSignature)) { - ui_display_node *pAbiDispNode; - pAbiDispNode = ui_create_display_node(EvmFunctionTitle, - strnlen(EvmFunctionTitle, 100), - EvmFunctionSignature, - strnlen(EvmFunctionSignature, 100)); - - // if (current_display_node == NULL) { - if (true) { - // current_display_node = pAbiDispNode; - } else { - ui_display_node *temp; // = current_display_node; - while (temp->next != NULL) { - temp = temp->next; - } - temp->next = pAbiDispNode; - } - } - - return numArgsInFunction; -} - -uint8_t ETH_ExtractArguments(const uint8_t *pAbiPayload, - const uint64_t sizeOfPayload) { - uint8_t returnCode = ETH_BAD_ARGUMENTS; - - /* Size of transaction payload must be atleast EVM_FUNC_SIGNATURE_LENGTH */ - if ((NULL == pAbiPayload) || (EVM_FUNC_SIGNATURE_LENGTH > sizeOfPayload)) { - return returnCode; - } - - uint8_t *pCurrHeadPtr = (uint8_t *)pAbiPayload; - - /** - * Detect if the ethereum unsigned txn payload includes a function that - * we can decode - * pArgumentAbiType will hold pointer to array with information regarding - * the types of argument corresponding to a function signature - */ - uint32_t functionTag = U32_READ_BE_ARRAY(pCurrHeadPtr); - uint8_t numArgsInFunction = 0; - Abi_Type_e const *pArgumentAbiType = NULL; - - numArgsInFunction = ETH_DetectFunction(functionTag, &pArgumentAbiType); - - /** - * If pArgumentAbiType is NULL, that means ETH_DetectFunction did not - * detect a supported function - * Therefore we should return from here - */ - if (NULL == pArgumentAbiType) { - returnCode = ETH_UTXN_FUNCTION_NOT_FOUND; - return returnCode; - } - - /* Increment pCurrHeadPtr to point to first argument */ - pCurrHeadPtr += EVM_FUNC_SIGNATURE_LENGTH; - - /** - * Save the base address of the first argument; it will be required in case - * of any dynamic element encoded in ABI format as the offset is calculated - * from the base of the first argument. - */ - const uint8_t *pPayloadBasePtr = pCurrHeadPtr; - uint8_t currArgument; - - for (currArgument = 0; currArgument < numArgsInFunction; currArgument++) { - /* Ensure that we are reading from within the bounds */ - if (UTIL_IN_BOUNDS != UTIL_CheckBound(pAbiPayload, - sizeOfPayload, - pCurrHeadPtr, - ABI_ELEMENT_SZ_IN_BYTES)) { - returnCode = ETH_UTXN_BAD_PAYLOAD; - break; - } - - ui_display_node *pAbiDispNode = NULL; - - /* Check if we are reading a dynamic or static element */ - if (Abi_bytes_dynamic_e <= pArgumentAbiType[currArgument]) { - uint8_t *pDynamicDataPtr = NULL; - uint8_t abiReturnCode = ABI_PROCESS_INCOMPLETE; - uint32_t numBytesReturned = 0; - - /* Get the information regarding dynamic data types */ - abiReturnCode = - Abi_DynamicHelp(pArgumentAbiType[currArgument], - pCurrHeadPtr, - pPayloadBasePtr, - (sizeOfPayload - EVM_FUNC_SIGNATURE_LENGTH), - &numBytesReturned, - &pDynamicDataPtr); - - /** - * If abiReturnCode is not ABI_PROCESS_COMPLETE, that means - * the function spotted an invalid argument during the call, or - * the payload was not good as bounds check failed internally - */ - if ((ABI_PROCESS_COMPLETE != abiReturnCode) || - (NULL == pDynamicDataPtr)) { - returnCode = ETH_UTXN_BAD_PAYLOAD; - break; - } - - /** - * Handle stringify based on dynamic data type - * Abi_bytes_dynamic_e can be handled directly using number of bytes - * Abi_uint256_array_dynamic_e needs to be handled in a loop, for each - * uint256 bit data - */ - if (Abi_bytes_dynamic_e == pArgumentAbiType[currArgument]) { - pAbiDispNode = - ABI_Stringify(Abi_bytes_e, pDynamicDataPtr, numBytesReturned); - } else if (Abi_uint256_array_dynamic_e == - pArgumentAbiType[currArgument]) { - uint32_t item; - for (item = 0; item < numBytesReturned; item++) { - uint8_t *pStaticData = - (uint8_t *)(pDynamicDataPtr + (ABI_ELEMENT_SZ_IN_BYTES * item)); - - pAbiDispNode = ABI_Stringify(Abi_uint256_e, pStaticData, 0); - } - } - } else /* Static elements can be stringified straight away */ - { - pAbiDispNode = - ABI_Stringify(pArgumentAbiType[currArgument], pCurrHeadPtr, 0); - } - - pCurrHeadPtr += ABI_ELEMENT_SZ_IN_BYTES; - returnCode = ETH_UTXN_ABI_DECODE_OK; - - // if (current_display_node == NULL) { - if (true) { - // current_display_node = pAbiDispNode; - } else { - ui_display_node *temp; // = current_display_node; - while (temp->next != NULL) { - temp = temp->next; - } - temp->next = pAbiDispNode; - } - } - - return returnCode; -} diff --git a/common/coin_support/eth.h b/common/coin_support/eth.h index da510199..d8448b3b 100644 --- a/common/coin_support/eth.h +++ b/common/coin_support/eth.h @@ -48,11 +48,6 @@ #define ETH_COIN_VERSION 0x00000000 -#define ETH_UTXN_ABI_DECODE_OK (0xAA) -#define ETH_UTXN_BAD_PAYLOAD (0x11) -#define ETH_UTXN_FUNCTION_NOT_FOUND (0x11) -#define ETH_BAD_ARGUMENTS (0x22) - /// Enum used to differentiate between a single val, string of bytes and list of /// strings during rlp decoding/encoding in raw eth byte array typedef enum { NONE, STRING, LIST } seq_type; @@ -148,21 +143,6 @@ void sig_unsigned_byte_array(const uint8_t *eth_unsigned_txn_byte_array, */ void eth_init_msg_data(MessageData *msg_data); -/** - * @brief This function extracts Abi encoded arguments for EVM functions into UI - * compatible nodes ui_display_node(s) - * - * @param pAbiPayload Pointer to start of payload of the EVM transaction - * @param sizeOfUTxn Size of payload of the EVM transaction - * @return uint8_t Depicts the status of operation for this function - * ETH_BAD_ARGUMENTS: If any argument is invalid - * ETH_UTXN_FUNCTION_NOT_FOUND: If a function NOT supported by X1 wallet is in - * the EVM tx ETH_UTXN_BAD_PAYLOAD: If a payload contains invalid data - * ETH_UTXN_ABI_DECODE_OK: If the arguments are extracted successfully - */ -uint8_t ETH_ExtractArguments(const uint8_t *pAbiPayload, - const uint64_t sizeOfPayload); - void eth_sign_msg_data(const MessageData *msg_data, const txn_metadata *transaction_metadata, const char *mnemonics, diff --git a/common/cypherock-common b/common/cypherock-common index 4bea8c6d..5b14bc93 160000 --- a/common/cypherock-common +++ b/common/cypherock-common @@ -1 +1 @@ -Subproject commit 4bea8c6dedd7b0b7cc5cf56f30c47395fe9a594d +Subproject commit 5b14bc93b48f4c37887c81b292bc124fb50035e3 diff --git a/common/proto-options/evm/sign_txn.options b/common/proto-options/evm/sign_txn.options index e4372cd9..9409f64f 100644 --- a/common/proto-options/evm/sign_txn.options +++ b/common/proto-options/evm/sign_txn.options @@ -1,7 +1,6 @@ # Options for file common/cypherock-common/proto/evm/sign_txn.proto evm.SignTxnInitiateRequest.walletId type:FT_STATIC max_size:32 fixed_length:true evm.SignTxnInitiateRequest.derivationPath type:FT_STATIC max_count:5 fixed_length:true -evm.SignTxnInitiateRequest.token_symbol type:FT_STATIC max_size:16 fixed_length:true evm.SignTxnSignatureResponse.r type:FT_STATIC max_size:32 fixed_length:true evm.SignTxnSignatureResponse.s type:FT_STATIC max_size:32 fixed_length:true evm.SignTxnSignatureResponse.v type:FT_STATIC max_size:1 fixed_length:true diff --git a/src/level_four/core/controller/send_transaction_controller_eth.c b/src/level_four/core/controller/send_transaction_controller_eth.c index 8a38dbfc..49d300d9 100644 --- a/src/level_four/core/controller/send_transaction_controller_eth.c +++ b/src/level_four/core/controller/send_transaction_controller_eth.c @@ -82,8 +82,8 @@ evm_unsigned_txn eth_unsigned_txn_ptr = { .to_address = {0}, .value_size = {0}, .value = {0}, - .payload_size = 0, - .payload = NULL, + .data_size = 0, + .data = NULL, .chain_id_size = {0}, .chain_id = {0}, .dummy_r = {0}, @@ -117,14 +117,6 @@ void send_transaction_controller_eth() { } } break; - case SEND_TXN_UNSIGNED_TXN_RECEIVED_ETH: { - if (eth_unsigned_txn_ptr.payload_status == - PAYLOAD_CONTRACT_NOT_WHITELISTED) - flow_level.level_three = SEND_TXN_VERIFY_CONTRACT_ADDRESS; - else - flow_level.level_three = SEND_TXN_VERIFY_BLIND_SIGNING_ETH; - } break; - case SEND_TXN_VERIFY_BLIND_SIGNING_ETH: { flow_level.level_three = SEND_TXN_VERIFY_DERIVATION_PATH; } break; @@ -145,16 +137,6 @@ void send_transaction_controller_eth() { flow_level.level_three = SEND_TXN_CALCULATE_AMOUNT_ETH; } break; - case SEND_TXN_CALCULATE_AMOUNT_ETH: { - if ((!evm_is_token_whitelisted) && - (eth_unsigned_txn_ptr.payload_status != PAYLOAD_ABSENT) && - (is_zero(eth_unsigned_txn_ptr.value, - eth_unsigned_txn_ptr.value_size[0]))) - flow_level.level_three = SEND_TXN_VERIFY_RECEIPT_FEES_ETH; - else - flow_level.level_three = SEND_TXN_VERIFY_RECEIPT_AMOUNT_ETH; - } break; - case SEND_TXN_VERIFY_RECEIPT_AMOUNT_ETH: { flow_level.level_three = SEND_TXN_VERIFY_RECEIPT_FEES_ETH; } break; diff --git a/src/level_four/core/tasks/send_transaction_tasks_eth.c b/src/level_four/core/tasks/send_transaction_tasks_eth.c index 7af94959..e281c43b 100644 --- a/src/level_four/core/tasks/send_transaction_tasks_eth.c +++ b/src/level_four/core/tasks/send_transaction_tasks_eth.c @@ -94,16 +94,6 @@ void send_transaction_tasks_eth() { mark_event_over(); } break; - case SEND_TXN_UNSIGNED_TXN_RECEIVED_ETH: { - if (eth_unsigned_txn_ptr.payload_status == - PAYLOAD_CONTRACT_NOT_WHITELISTED) { - instruction_scr_destructor(); - delay_scr_init(ui_text_unverified_contract, DELAY_TIME); - } else { - mark_event_over(); - } - } break; - case SEND_TXN_VERIFY_CONTRACT_ADDRESS: { char address[43]; address[0] = '0'; @@ -121,34 +111,6 @@ void send_transaction_tasks_eth() { address_scr_init(top_heading, display, true); } break; - case SEND_TXN_VERIFY_BLIND_SIGNING_ETH: { - if (eth_unsigned_txn_ptr.payload_status == - PAYLOAD_SIGNATURE_NOT_WHITELISTED) { - char display[125] = {0}; - instruction_scr_destructor(); - snprintf(display, - sizeof(display), - "%s Blind Signing\nProceed at your own risk!", - LV_SYMBOL_WARNING); - confirm_scr_init(display); - } else { - mark_event_over(); - } - } break; - - case SEND_TXN_VERIFY_DERIVATION_PATH: { - if (eth_unsigned_txn_ptr.payload_status == - PAYLOAD_SIGNATURE_NOT_WHITELISTED) { - char display[125] = {0}; - char path[128] = {0}; - instruction_scr_destructor(); - snprintf(display, sizeof(display), "Verify Derivation Path\n%s", path); - confirm_scr_init(display); - } else { - mark_event_over(); - } - } break; - case SEND_TXN_VERIFY_TXN_NONCE_ETH: { char nonce_hex_str[ETH_NONCE_SIZE_BYTES * 2 + 1] = {'\0'}; uint8_t nonce_dec_str[ETH_NONCE_SIZE_BYTES * 3] = {0}; @@ -196,10 +158,7 @@ void send_transaction_tasks_eth() { else bech32_addr_encode( address, "one", address_bytes, sizeof(address_bytes)); - snprintf(top_heading, - sizeof(top_heading), - "%s", - eth_get_address_title(ð_unsigned_txn_ptr)); + snprintf(top_heading, sizeof(top_heading), "%s", ""); snprintf(display, sizeof(display), "%s%s", ui_text_20_spaces, address); address_scr_init(top_heading, display, true); } break; @@ -217,7 +176,6 @@ void send_transaction_tasks_eth() { memzero(amount_string, sizeof(amount_string)); uint8_t len = 0, i = 0, j = 0; - len = eth_get_value(ð_unsigned_txn_ptr, amount_string); uint8_t decimal_val_s[ETH_VALUE_SIZE_BYTES * 3] = {0}; if (sizeof(decimal_val_s) / sizeof(decimal_val_s[0]) > UINT8_MAX) { LOG_ERROR("0xxx#"); diff --git a/tests/apps/evm_app/evm_txn_tests.c b/tests/apps/evm_app/evm_txn_tests.c index 40af1910..047e7894 100644 --- a/tests/apps/evm_app/evm_txn_tests.c +++ b/tests/apps/evm_app/evm_txn_tests.c @@ -109,8 +109,6 @@ TEST(evm_txn_test, evm_txn_eth_transfer) { .wallet_id = {}, .address_format = EVM_DEFAULT, .transaction_size = 44, - .token_symbol = "", - .is_token_transfer = false, }}}; evm_query_t query1 = {.which_request = 2, .sign_txn = {.which_request = 2, @@ -152,8 +150,6 @@ TEST(evm_txn_test, evm_txn_usdt_transfer) { .wallet_id = {}, .address_format = EVM_DEFAULT, .transaction_size = 109, - .token_symbol = "USDT", - .is_token_transfer = true, }}}; evm_query_t query1 = {.which_request = 2, .sign_txn = {.which_request = 2, @@ -198,8 +194,6 @@ TEST(evm_txn_test, evm_txn_haka_transfer) { .wallet_id = {}, .address_format = EVM_DEFAULT, .transaction_size = 110, - .token_symbol = "HAKA", - .is_token_transfer = true, }}}; evm_query_t query1 = {.which_request = 2, .sign_txn = {.which_request = 2, @@ -244,8 +238,6 @@ TEST(evm_txn_test, evm_txn_blind_signing) { .wallet_id = {}, .address_format = EVM_DEFAULT, .transaction_size = 1489, - .token_symbol = "", - .is_token_transfer = false, }}}; evm_query_t query1 = {.which_request = 2, .sign_txn = {.which_request = 2,