From 4f2cef2e20371dbb31f343b7ecb6e4c75a7fccd9 Mon Sep 17 00:00:00 2001 From: TamtamHero <10632523+TamtamHero@users.noreply.github.com> Date: Mon, 5 Oct 2020 18:22:42 +0200 Subject: [PATCH] Add Compound support as an internal plugin --- src/eth_plugin_handler.h | 1 + src/eth_plugin_internal.c | 18 ++ src/eth_plugin_internal.h | 10 +- src/eth_plugin_ui.c | 2 +- src_features/signTx/logic_signTx.c | 5 +- src_plugins/compound/compound_plugin.c | 224 +++++++++++++++++++++++++ src_plugins/erc20/erc20_plugin.c | 4 +- 7 files changed, 254 insertions(+), 10 deletions(-) create mode 100644 src_plugins/compound/compound_plugin.c diff --git a/src/eth_plugin_handler.h b/src/eth_plugin_handler.h index e8a1a25..338017b 100644 --- a/src/eth_plugin_handler.h +++ b/src/eth_plugin_handler.h @@ -12,6 +12,7 @@ void eth_plugin_prepare_query_contract_UI(ethQueryContractUI_t *queryContractUI, int eth_plugin_perform_init(uint8_t *contractAddress, ethPluginInitContract_t *init); // NULL for cached address, or base contract address int eth_plugin_call(uint8_t *contractAddress, int method, void *parameter); +int compound_plugin_call(uint8_t *contractAddress, int method, void *parameter); void plugin_ui_start(void); diff --git a/src/eth_plugin_internal.c b/src/eth_plugin_internal.c index 1bc89b8..ca1fe37 100644 --- a/src/eth_plugin_internal.c +++ b/src/eth_plugin_internal.c @@ -1,6 +1,7 @@ #include "eth_plugin_internal.h" void erc20_plugin_call(int message, void *parameters); +void compound_plugin_call(int message, void *parameters); void starkware_plugin_call(int message, void *parameters); static const uint8_t const ERC20_TRANSFER_SELECTOR[SELECTOR_SIZE] = { 0xa9, 0x05, 0x9c, 0xbb }; @@ -10,6 +11,16 @@ const uint8_t* const ERC20_SELECTORS[NUM_ERC20_SELECTORS] = { ERC20_TRANSFER_SELECTOR, ERC20_APPROVE_SELECTOR }; +static const uint8_t const COMPOUND_REDEEM_UNDERLYING_SELECTOR[SELECTOR_SIZE] = { 0x85, 0x2a, 0x12, 0xe3 }; +static const uint8_t const COMPOUND_REDEEM_SELECTOR[SELECTOR_SIZE] = { 0xdb, 0x00, 0x6a, 0x75 }; +static const uint8_t const COMPOUND_MINT_SELECTOR[SELECTOR_SIZE] = { 0xa0, 0x71, 0x2d, 0x68 }; +static const uint8_t const CETH_MINT_SELECTOR[SELECTOR_SIZE] = { 0x12, 0x49, 0xc5, 0x8b }; + +const uint8_t* const COMPOUND_SELECTORS[NUM_COMPOUND_SELECTORS] = { + COMPOUND_REDEEM_UNDERLYING_SELECTOR, COMPOUND_REDEEM_SELECTOR, + COMPOUND_MINT_SELECTOR, CETH_MINT_SELECTOR +}; + #ifdef HAVE_STARKWARE static const uint8_t const STARKWARE_REGISTER_ID[SELECTOR_SIZE] = { 0x76, 0x57, 0x18, 0xd7 }; @@ -42,6 +53,13 @@ const internalEthPlugin_t const INTERNAL_ETH_PLUGINS[NUM_INTERNAL_PLUGINS] = { erc20_plugin_call }, + { + COMPOUND_SELECTORS, + 4, + "-cmpd", + compound_plugin_call + }, + #ifdef HAVE_STARKWARE { diff --git a/src/eth_plugin_internal.h b/src/eth_plugin_internal.h index 17e9da3..38540ab 100644 --- a/src/eth_plugin_internal.h +++ b/src/eth_plugin_internal.h @@ -12,20 +12,22 @@ typedef struct internalEthPlugin_t { } internalEthPlugin_t; #define NUM_ERC20_SELECTORS 2 - extern const uint8_t* const ERC20_SELECTORS[NUM_ERC20_SELECTORS]; +#define NUM_COMPOUND_SELECTORS 4 +extern const uint8_t* const COMPOUND_SELECTORS[NUM_COMPOUND_SELECTORS]; + + #ifdef HAVE_STARKWARE -#define NUM_INTERNAL_PLUGINS 2 +#define NUM_INTERNAL_PLUGINS 3 #define NUM_STARKWARE_SELECTORS 10 - extern const uint8_t* const STARKWARE_SELECTORS[NUM_STARKWARE_SELECTORS]; #else -#define NUM_INTERNAL_PLUGINS 1 +#define NUM_INTERNAL_PLUGINS 2 #endif diff --git a/src/eth_plugin_ui.c b/src/eth_plugin_ui.c index d38642f..281de79 100644 --- a/src/eth_plugin_ui.c +++ b/src/eth_plugin_ui.c @@ -134,7 +134,7 @@ UX_STEP_NOCB_INIT( bnnn_paging, plugin_ui_compute_fees(), { - .title = "Max fees", + .title = "Max Fees", .text = strings.common.maxFee }); diff --git a/src_features/signTx/logic_signTx.c b/src_features/signTx/logic_signTx.c index 90bfcc3..b4c7383 100644 --- a/src_features/signTx/logic_signTx.c +++ b/src_features/signTx/logic_signTx.c @@ -51,9 +51,8 @@ customStatus_e customProcessor(txContext_t *context) { dataContext.tokenContext.fieldOffset = 0; copyTxData(context, NULL, 4); if (context->currentFieldLength == 4) { - dataContext.tokenContext.fieldIndex++; - return CUSTOM_HANDLED; - } + return CUSTOM_NOT_HANDLED; + } } } uint32_t blockSize; diff --git a/src_plugins/compound/compound_plugin.c b/src_plugins/compound/compound_plugin.c new file mode 100644 index 0000000..815d71c --- /dev/null +++ b/src_plugins/compound/compound_plugin.c @@ -0,0 +1,224 @@ +#include +#include "eth_plugin_interface.h" +#include "shared_context.h" // TODO : rewrite as independant code +#include "eth_plugin_internal.h" // TODO : rewrite as independant code +#include "utils.h" + +typedef enum { + COMPOUND_REDEEM_UNDERLYING = 0, + COMPOUND_REDEEM, + COMPOUND_MINT, + CETH_MINT +} compoundSelector_t; + +static const uint8_t COMPOUND_EXPECTED_DATA_SIZE[] = { + 4 + 32, + 4 + 32, + 4 + 32, + 4, +}; + +// redeemUnderlying : redeemAmount (32) +// redeem underlying token +// redeem : redeemTokens (32) +// redeem Ctoken +// mint : mintAmount (32) +// lend some token +// mint : +// lend some Ether + +typedef struct compound_parameters_t { + uint8_t selectorIndex; + uint8_t amount[32]; + uint8_t ticker_1[MAX_TICKER_LEN]; + uint8_t decimals; +} compound_parameters_t; + +typedef struct underlying_asset_decimals_t { + char c_ticker[MAX_TICKER_LEN]; + uint8_t decimals; +} underlying_asset_decimals_t; + + + +/* Sadly, we don't have the information about the underlying asset's decimals, which can differ from the cToken decimals. +Therefore, we hardcode a binding table. If Compound adds a lot of token in the future, we will have to move to a CAL +based architecture instead, as this one doesn't scale well.*/ +#define NUM_COMPOUND_BINDINGS 9 +const underlying_asset_decimals_t const UNDERLYING_ASSET_DECIMALS[NUM_COMPOUND_BINDINGS] = { + {"cDAI", 18}, + {"CETH", 18}, + {"CUSDC", 6}, + {"CZRX", 18}, + {"CUSDT", 6}, + {"CBTC", 8}, + {"CBAT", 18}, + {"CREP", 18}, + {"cSAI", 18}, +}; + +bool get_underlying_asset_decimals(char* compound_ticker, uint8_t* out_decimals){ + for(size_t i = 0; i < NUM_COMPOUND_BINDINGS; i++){ + underlying_asset_decimals_t* binding = (underlying_asset_decimals_t *)PIC(&UNDERLYING_ASSET_DECIMALS[i]); + if (strncmp(binding->c_ticker, compound_ticker, strnlen(binding->c_ticker, MAX_TICKER_LEN)) == 0){ + *out_decimals = binding->decimals; + return true; + } + } + return false; +} + +void compound_plugin_call(int message, void *parameters) { + + switch(message) { + case ETH_PLUGIN_INIT_CONTRACT: { + ethPluginInitContract_t *msg = (ethPluginInitContract_t*)parameters; + compound_parameters_t *context = (compound_parameters_t*)msg->pluginContext; + size_t i; + for (i=0; iselector, SELECTOR_SIZE) == 0) { + context->selectorIndex = i; + break; + } + } + // enforce that ETH amount should be 0, except in ceth.mint case + if (!allzeroes(msg->pluginSharedRO->txContent->value.value, 32)){ + if(context->selectorIndex != CETH_MINT){ + msg->result = ETH_PLUGIN_RESULT_ERROR; + } + } + if(context->selectorIndex == CETH_MINT){ + // ETH amount 0x1234 is stored 0x12340000...000 instead of 0x00....001234, so we strip the following zeroes when copying + memset(context->amount, 0, sizeof(context->amount)); + memmove(context->amount + sizeof(context->amount) - msg->pluginSharedRO->txContent->value.length, msg->pluginSharedRO->txContent->value.value, 32); + } + if (i == NUM_COMPOUND_SELECTORS) { + PRINTF("Unknown selector %.*H\n", SELECTOR_SIZE, msg->selector); + break; + } + if (msg->dataSize != COMPOUND_EXPECTED_DATA_SIZE[context->selectorIndex]) { + PRINTF("Unexpected data size for command %d expected %d got %d\n", context->selectorIndex, + COMPOUND_EXPECTED_DATA_SIZE[context->selectorIndex], msg->dataSize); + break; + } + PRINTF("compound plugin init\n"); + msg->result = ETH_PLUGIN_RESULT_OK; + } + break; + + case ETH_PLUGIN_PROVIDE_PARAMETER : { + ethPluginProvideParameter_t *msg = (ethPluginProvideParameter_t*)parameters; + compound_parameters_t *context = (compound_parameters_t*)msg->pluginContext; + PRINTF("compound plugin provide parameter %d %.*H\n", msg->parameterOffset, 32, msg->parameter); + if (context->selectorIndex != CETH_MINT){ + switch(msg->parameterOffset) { + case 4: + memmove(context->amount, msg->parameter, 32); + msg->result = ETH_PLUGIN_RESULT_OK; + break; + default: + PRINTF("Unhandled parameter offset\n"); + break; + } + } + } + break; + + case ETH_PLUGIN_FINALIZE: { + ethPluginFinalize_t *msg = (ethPluginFinalize_t*)parameters; + PRINTF("compound plugin finalize\n"); + msg->tokenLookup1 = msg->pluginSharedRO->txContent->destination; + msg->numScreens = 2; + msg->uiType = ETH_UI_TYPE_GENERIC; + msg->result = ETH_PLUGIN_RESULT_OK; + } + break; + + case ETH_PLUGIN_PROVIDE_TOKEN: { + ethPluginProvideToken_t *msg = (ethPluginProvideToken_t*)parameters; + compound_parameters_t *context = (compound_parameters_t*)msg->pluginContext; + PRINTF("compound plugin provide token: %d\n", (msg->token1 != NULL)); + if (msg->token1 != NULL) { + strcpy((char *)context->ticker_1, (char *)msg->token1->ticker); + switch (context->selectorIndex) + { + case COMPOUND_REDEEM_UNDERLYING: + case COMPOUND_MINT: + case CETH_MINT: + msg->result = get_underlying_asset_decimals(context->ticker_1, &context->decimals) ? ETH_PLUGIN_RESULT_OK : ETH_PLUGIN_RESULT_FALLBACK; + break; + + // Only case where we use the compound contract decimals + case COMPOUND_REDEEM: + context->decimals = msg->token1->decimals; + msg->result = ETH_PLUGIN_RESULT_OK; + break; + + default: + msg->result = ETH_PLUGIN_RESULT_FALLBACK; + break; + } + } + else { + msg->result = ETH_PLUGIN_RESULT_FALLBACK; + } + } + break; + + case ETH_PLUGIN_QUERY_CONTRACT_ID: { + ethQueryContractID_t *msg = (ethQueryContractID_t*)parameters; + compound_parameters_t *context = (compound_parameters_t*)msg->pluginContext; + strcpy(msg->name, "Type"); + switch (context->selectorIndex) + { + case COMPOUND_REDEEM_UNDERLYING: + case COMPOUND_REDEEM: + strcpy(msg->version, "Redeem"); + break; + + case COMPOUND_MINT: + case CETH_MINT: + strcpy(msg->version, "Lend"); + break; + + default: + break; + } + strcat(msg->version, " Assets"); + msg->result = ETH_PLUGIN_RESULT_OK; + } + break; + + case ETH_PLUGIN_QUERY_CONTRACT_UI: { + ethQueryContractUI_t *msg = (ethQueryContractUI_t*)parameters; + compound_parameters_t *context = (compound_parameters_t*)msg->pluginContext; + switch(msg->screenIndex) { + case 0: { + strcpy(msg->title, "Amount"); + char * ticker_ptr = (char *)context->ticker_1; + /* skip "c" in front of cToken unless we use "redeem", as + redeem is the only operation dealing with a cToken amount */ + if(context->selectorIndex != COMPOUND_REDEEM){ + ticker_ptr++; + } + amountToString(context->amount, sizeof(context->amount), context->decimals, ticker_ptr, msg->msg, 100); + msg->result = ETH_PLUGIN_RESULT_OK; + } + break; + + case 1: + strcpy(msg->title, "Contract"); + strcpy(msg->msg, "Compound "); + strcat(msg->msg, (char *)context->ticker_1 + 1); // remove the 'c' char at beginning of compound ticker + msg->result = ETH_PLUGIN_RESULT_OK; + break; + default: + break; + } + } + break; + + default: + PRINTF("Unhandled message %d\n", message); + } +} diff --git a/src_plugins/erc20/erc20_plugin.c b/src_plugins/erc20/erc20_plugin.c index fab3df0..552f7dc 100644 --- a/src_plugins/erc20/erc20_plugin.c +++ b/src_plugins/erc20/erc20_plugin.c @@ -153,8 +153,8 @@ void erc20_plugin_call(int message, void *parameters) { case ETH_PLUGIN_QUERY_CONTRACT_ID: { ethQueryContractID_t *msg = (ethQueryContractID_t*)parameters; - strcpy(msg->name, "Approve"); - strcpy(msg->version, ""); + strcpy(msg->name, "Type"); + strcpy(msg->version, "Approve"); msg->result = ETH_PLUGIN_RESULT_OK; } break;