diff --git a/ethereum-plugin-sdk b/ethereum-plugin-sdk index b9777e7a..1fe4085d 160000 --- a/ethereum-plugin-sdk +++ b/ethereum-plugin-sdk @@ -1 +1 @@ -Subproject commit b9777e7a81a9c33b3f8997ffa23396fa2b3f861d +Subproject commit 1fe4085d04a88f4238103a4ed3db1484fdb69c63 diff --git a/src/boilerplate_plugin.h b/src/boilerplate_plugin.h index 676da881..eb329b6e 100644 --- a/src/boilerplate_plugin.h +++ b/src/boilerplate_plugin.h @@ -59,10 +59,3 @@ typedef struct context_t { // Piece of code that will check that the above structure is not bigger than 5 * 32. Do not remove // this check. _Static_assert(sizeof(context_t) <= 5 * 32, "Structure of parameters too big."); - -void handle_provide_parameter(void *parameters); -void handle_query_contract_ui(void *parameters); -void handle_init_contract(void *parameters); -void handle_finalize(void *parameters); -void handle_provide_token(void *parameters); -void handle_query_contract_id(void *parameters); \ No newline at end of file diff --git a/src/contract.c b/src/contract.c new file mode 100644 index 00000000..48560b37 --- /dev/null +++ b/src/contract.c @@ -0,0 +1,32 @@ +/******************************************************************************* + * Plugin Boilerplate + * (c) 2023 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +#include +#include "boilerplate_plugin.h" + +// List of selectors supported by this plugin. +// EDIT THIS: Adapt the variable names and change the `0x` values to match your selectors. +static const uint32_t SWAP_EXACT_ETH_FOR_TOKENS_SELECTOR = 0x7ff36ab5; +static const uint32_t BOILERPLATE_DUMMY_SELECTOR_2 = 0x13374242; + +// Array of all the different boilerplate selectors. Make sure this follows the same order as the +// enum defined in `boilerplate_plugin.h` +// EDIT THIS: Use the names of the array declared above. +const uint32_t BOILERPLATE_SELECTORS[NUM_SELECTORS] = { + SWAP_EXACT_ETH_FOR_TOKENS_SELECTOR, + BOILERPLATE_DUMMY_SELECTOR_2, +}; diff --git a/src/handle_init_contract.c b/src/handle_init_contract.c index d79fff89..3154cc1e 100644 --- a/src/handle_init_contract.c +++ b/src/handle_init_contract.c @@ -1,20 +1,8 @@ #include "boilerplate_plugin.h" - -static int find_selector(uint32_t selector, const uint32_t *selectors, size_t n, selector_t *out) { - for (selector_t i = 0; i < n; i++) { - if (selector == selectors[i]) { - *out = i; - return 0; - } - } - return -1; -} +#include "utils.h" // Called once to init. -void handle_init_contract(void *parameters) { - // Cast the msg to the type of structure we expect (here, ethPluginInitContract_t). - ethPluginInitContract_t *msg = (ethPluginInitContract_t *) parameters; - +void handle_init_contract(ethPluginInitContract_t *msg) { // Make sure we are running a compatible version. if (msg->interfaceVersion != ETH_PLUGIN_INTERFACE_VERSION_LATEST) { // If not the case, return the `UNAVAILABLE` status. @@ -35,11 +23,19 @@ void handle_init_contract(void *parameters) { // Initialize the context (to 0). memset(context, 0, sizeof(*context)); - uint32_t selector = U4BE(msg->selector, 0); - if (find_selector(selector, BOILERPLATE_SELECTORS, NUM_SELECTORS, &context->selectorIndex)) { + size_t index; + if (!find_selector(U4BE(msg->selector, 0), BOILERPLATE_SELECTORS, NUM_SELECTORS, &index)) { + PRINTF("Error: selector not found!\n"); msg->result = ETH_PLUGIN_RESULT_UNAVAILABLE; return; } + context->selectorIndex = index; + // check for overflow + if ((size_t) context->selectorIndex != index) { + PRINTF("Error: overflow detected on selector index!\n"); + msg->result = ETH_PLUGIN_RESULT_ERROR; + return; + } // Set `next_param` to be the first field we expect to parse. // EDIT THIS: Adapt the `cases`, and set the `next_param` to be the first parameter you expect diff --git a/src/handle_provide_parameter.c b/src/handle_provide_parameter.c index cdd70098..f51a0061 100644 --- a/src/handle_provide_parameter.c +++ b/src/handle_provide_parameter.c @@ -41,8 +41,7 @@ static void handle_swap_exact_eth_for_tokens(ethPluginProvideParameter_t *msg, c } } -void handle_provide_parameter(void *parameters) { - ethPluginProvideParameter_t *msg = (ethPluginProvideParameter_t *) parameters; +void handle_provide_parameter(ethPluginProvideParameter_t *msg) { context_t *context = (context_t *) msg->pluginContext; // We use `%.*H`: it's a utility function to print bytes. You first give // the number of bytes you wish to print (in this case, `PARAMETER_LENGTH`) and then @@ -66,4 +65,4 @@ void handle_provide_parameter(void *parameters) { msg->result = ETH_PLUGIN_RESULT_ERROR; break; } -} \ No newline at end of file +} diff --git a/src/handle_provide_token.c b/src/handle_provide_token.c index 9ae94bd5..5e688505 100644 --- a/src/handle_provide_token.c +++ b/src/handle_provide_token.c @@ -3,8 +3,7 @@ // EDIT THIS: Adapt this function to your needs! Remember, the information for tokens are held in // `msg->token1` and `msg->token2`. If those pointers are `NULL`, this means the ethereum app didn't // find any info regarding the requested tokens! -void handle_provide_token(void *parameters) { - ethPluginProvideInfo_t *msg = (ethPluginProvideInfo_t *) parameters; +void handle_provide_token(ethPluginProvideInfo_t *msg) { context_t *context = (context_t *) msg->pluginContext; if (msg->item1) { @@ -26,4 +25,4 @@ void handle_provide_token(void *parameters) { // msg->additionalScreens = 1; } msg->result = ETH_PLUGIN_RESULT_OK; -} \ No newline at end of file +} diff --git a/src/handle_query_contract_id.c b/src/handle_query_contract_id.c index b40c8107..9c31e502 100644 --- a/src/handle_query_contract_id.c +++ b/src/handle_query_contract_id.c @@ -1,8 +1,7 @@ #include "boilerplate_plugin.h" // Sets the first screen to display. -void handle_query_contract_id(void *parameters) { - ethQueryContractID_t *msg = (ethQueryContractID_t *) parameters; +void handle_query_contract_id(ethQueryContractID_t *msg) { const context_t *context = (const context_t *) msg->pluginContext; // msg->name will be the upper sentence displayed on the screen. // msg->version will be the lower sentence displayed on the screen. @@ -18,4 +17,4 @@ void handle_query_contract_id(void *parameters) { PRINTF("Selector index: %d not supported\n", context->selectorIndex); msg->result = ETH_PLUGIN_RESULT_ERROR; } -} \ No newline at end of file +} diff --git a/src/handle_query_contract_ui.c b/src/handle_query_contract_ui.c index 14028c62..dc9b0f77 100644 --- a/src/handle_query_contract_ui.c +++ b/src/handle_query_contract_ui.c @@ -5,7 +5,7 @@ // Set UI for the "Send" screen. // EDIT THIS: Adapt / remove this function to your needs. -static void set_send_ui(ethQueryContractUI_t *msg) { +static bool set_send_ui(ethQueryContractUI_t *msg) { strlcpy(msg->title, "Send", msg->titleLength); const uint8_t *eth_amount = msg->pluginSharedRO->txContent->value.value; @@ -13,12 +13,17 @@ static void set_send_ui(ethQueryContractUI_t *msg) { // Converts the uint256 number located in `eth_amount` to its string representation and // copies this to `msg->msg`. - amountToString(eth_amount, eth_amount_size, WEI_TO_ETHER, "ETH", msg->msg, msg->msgLength); + return amountToString(eth_amount, + eth_amount_size, + WEI_TO_ETHER, + "ETH", + msg->msg, + msg->msgLength); } // Set UI for "Receive" screen. // EDIT THIS: Adapt / remove this function to your needs. -static void set_receive_ui(ethQueryContractUI_t *msg, const context_t *context) { +static bool set_receive_ui(ethQueryContractUI_t *msg, const context_t *context) { strlcpy(msg->title, "Receive Min.", msg->titleLength); uint8_t decimals = context->decimals; @@ -30,17 +35,17 @@ static void set_receive_ui(ethQueryContractUI_t *msg, const context_t *context) ticker = msg->network_ticker; } - amountToString(context->amount_received, - sizeof(context->amount_received), - decimals, - ticker, - msg->msg, - msg->msgLength); + return amountToString(context->amount_received, + sizeof(context->amount_received), + decimals, + ticker, + msg->msg, + msg->msgLength); } // Set UI for "Beneficiary" screen. // EDIT THIS: Adapt / remove this function to your needs. -static void set_beneficiary_ui(ethQueryContractUI_t *msg, context_t *context) { +static bool set_beneficiary_ui(ethQueryContractUI_t *msg, context_t *context) { strlcpy(msg->title, "Beneficiary", msg->titleLength); // Prefix the address with `0x`. @@ -53,16 +58,16 @@ static void set_beneficiary_ui(ethQueryContractUI_t *msg, context_t *context) { // Get the string representation of the address stored in `context->beneficiary`. Put it in // `msg->msg`. - getEthAddressStringFromBinary( + return getEthAddressStringFromBinary( context->beneficiary, msg->msg + 2, // +2 here because we've already prefixed with '0x'. msg->pluginSharedRW->sha3, chainid); } -void handle_query_contract_ui(void *parameters) { - ethQueryContractUI_t *msg = (ethQueryContractUI_t *) parameters; +void handle_query_contract_ui(ethQueryContractUI_t *msg) { context_t *context = (context_t *) msg->pluginContext; + bool ret = false; // msg->title is the upper line displayed on the device. // msg->msg is the lower line displayed on the device. @@ -71,23 +76,20 @@ void handle_query_contract_ui(void *parameters) { memset(msg->title, 0, msg->titleLength); memset(msg->msg, 0, msg->msgLength); - msg->result = ETH_PLUGIN_RESULT_OK; - // EDIT THIS: Adapt the cases for the screens you'd like to display. switch (msg->screenIndex) { case 0: - set_send_ui(msg); + ret = set_send_ui(msg); break; case 1: - set_receive_ui(msg, context); + ret = set_receive_ui(msg, context); break; case 2: - set_beneficiary_ui(msg, context); + ret = set_beneficiary_ui(msg, context); break; // Keep this default: PRINTF("Received an invalid screenIndex\n"); - msg->result = ETH_PLUGIN_RESULT_ERROR; - return; } + msg->result = ret ? ETH_PLUGIN_RESULT_OK : ETH_PLUGIN_RESULT_ERROR; } diff --git a/src/main.c b/src/main.c deleted file mode 100644 index db970bfb..00000000 --- a/src/main.c +++ /dev/null @@ -1,155 +0,0 @@ -/******************************************************************************* - * Ethereum 2 Deposit Application - * (c) 2020 Ledger - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ********************************************************************************/ - -#include -#include -#include - -#include "os.h" -#include "cx.h" - -#include "glyphs.h" - -#include "boilerplate_plugin.h" - -// List of selectors supported by this plugin. -// EDIT THIS: Adapt the variable names and change the `0x` values to match your selectors. -static const uint32_t SWAP_EXACT_ETH_FOR_TOKENS_SELECTOR = 0x7ff36ab5; -static const uint32_t BOILERPLATE_DUMMY_SELECTOR_2 = 0x13374242; - -// Array of all the different boilerplate selectors. Make sure this follows the same order as the -// enum defined in `boilerplate_plugin.h` -// EDIT THIS: Use the names of the array declared above. -const uint32_t BOILERPLATE_SELECTORS[NUM_SELECTORS] = { - SWAP_EXACT_ETH_FOR_TOKENS_SELECTOR, - BOILERPLATE_DUMMY_SELECTOR_2, -}; - -// Function to dispatch calls from the ethereum app. -void dispatch_plugin_calls(int message, void *parameters) { - switch (message) { - case ETH_PLUGIN_INIT_CONTRACT: - handle_init_contract(parameters); - break; - case ETH_PLUGIN_PROVIDE_PARAMETER: - handle_provide_parameter(parameters); - break; - case ETH_PLUGIN_FINALIZE: - handle_finalize(parameters); - break; - case ETH_PLUGIN_PROVIDE_INFO: - handle_provide_token(parameters); - break; - case ETH_PLUGIN_QUERY_CONTRACT_ID: - handle_query_contract_id(parameters); - break; - case ETH_PLUGIN_QUERY_CONTRACT_UI: - handle_query_contract_ui(parameters); - break; - default: - PRINTF("Unhandled message %d\n", message); - break; - } -} - -void handle_query_ui_exception(unsigned int *args) { - switch (args[0]) { - case ETH_PLUGIN_QUERY_CONTRACT_UI: - ((ethQueryContractUI_t *) args[1])->result = ETH_PLUGIN_RESULT_ERROR; - break; - default: - break; - } -} - -// Calls the ethereum app. -void call_app_ethereum() { - unsigned int libcall_params[5]; - libcall_params[0] = (unsigned int) "Ethereum"; - libcall_params[1] = 0x100; - libcall_params[2] = RUN_APPLICATION; - libcall_params[3] = (unsigned int) NULL; -#ifdef HAVE_NBGL - caller_app_t capp; - const char name[] = APPNAME; - nbgl_icon_details_t icon_details; - uint8_t bitmap[sizeof(ICONBITMAP)]; - - memcpy(&icon_details, &ICONGLYPH, sizeof(ICONGLYPH)); - memcpy(&bitmap, &ICONBITMAP, sizeof(bitmap)); - icon_details.bitmap = (const uint8_t *) bitmap; - capp.name = (const char *) name; - capp.icon = &icon_details; - libcall_params[4] = (unsigned int) &capp; -#else - libcall_params[4] = (unsigned int) NULL; -#endif - os_lib_call((unsigned int *) &libcall_params); -} - -// Weird low-level black magic. No need to edit this. -__attribute__((section(".boot"))) int main(int arg0) { - // Exit critical section - __asm volatile("cpsie i"); - - // Ensure exception will work as planned - os_boot(); - - // Try catch block. Please read the docs for more information on how to use those! - BEGIN_TRY { - TRY { - // Low-level black magic. - check_api_level(CX_COMPAT_APILEVEL); - - // Check if we are called from the dashboard. - if (!arg0) { - // Called from dashboard, launch Ethereum app - call_app_ethereum(); - return 0; - } else { - // Not called from dashboard: called from the ethereum app! - const unsigned int *args = (const unsigned int *) arg0; - - // If `ETH_PLUGIN_CHECK_PRESENCE` is set, this means the caller is just trying to - // know whether this app exists or not. We can skip `dispatch_plugin_calls`. - if (args[0] != ETH_PLUGIN_CHECK_PRESENCE) { - dispatch_plugin_calls(args[0], (void *) args[1]); - } - } - } - CATCH_OTHER(e) { - switch (e) { - // These exceptions are only generated on handle_query_contract_ui() - case 0x6502: - case EXCEPTION_OVERFLOW: - handle_query_ui_exception((unsigned int *) arg0); - break; - default: - break; - } - PRINTF("Exception 0x%x caught\n", e); - } - FINALLY { - // Call `os_lib_end`, go back to the ethereum app. - os_lib_end(); - } - } - END_TRY; - - // Will not get reached. - return 0; -}