* First draft for erc721 token allowance * Split ui and provide parameters into their own files * Print txtype when not supported * fix compilation for erc721 * Use pluginType * Add debug statement in compound plugin * add debug error msg in plugin error * Add parameter parsing for all methods * Remove debug logs * Add SET_APPROVAL_FOR_ALL; Add correct parsing method on contract init * Add dst_size parameter to copy functions * Add query contract id code * format * Add UIs * update ethapp.asc * Change setExternalPlugin to setPlugin; Add support for ERC721 * clang-format * Fix typo Unconsistent -> Inconsistent * Add support for 721; use extraInfo * Add extraInfo to ethpluginQueryConractUI * Rename extraInfo to item * Add txFromEtherscan to tests * Add nft key and temp padding * Remove comments around HAVE_BYPASS_SIGNATURES * Rename TESTING_KEY to NFT_TESTING_KEY * Add comments regarding value of queryContractUI->item * Fix comment regarding method selector * Rename provideToken to provideInfo; Update plugin doc * fix caps of eth_plugin_prepare_provide_info * fix caps of handle_provide_info * Use verificationFn insead of hardcoded cx_ecdsa_verify * Add comments about nftInfo_t and tokenDefinition_t * Add erc721 test * Remove comment from plugin interface version * Fix network_ticker duplicate * Add setPlugin and provideNFTInfo to doc.asc * Add back setExternalPlugin; implement new setPlugin * Update plugin sdk * Call setPlugin instead of setExternalPlugin * setPlugin work without checking sig * Remove printf of displayed fees * Add working 721 test * Finalize ERC721 and add simple test * Display NFT address on set approval and operator * Support set approval for all for erc721 * Finish UI for set approval for all erc721 * Move copy_parameter and copy_address to eth_plugin_internal; Add tests for erc721 * update plugin sdk * Add erc1155 plugin and 1155 tests placeholder * Add restriction for AWS key and setPlugin * Add NOT_OLD_INTERNAL variant; Add erc_1155_plugin_call * Fixed compilation warnings (function pointer casting) Co-authored-by: pscott <scott.piriou@ledger.fr>
243 lines
9.6 KiB
C
243 lines
9.6 KiB
C
#include <string.h>
|
|
#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];
|
|
char 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 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; i < NUM_COMPOUND_SELECTORS; i++) {
|
|
if (memcmp((uint8_t *) PIC(COMPOUND_SELECTORS[i]), msg->selector, 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) {
|
|
PRINTF("Eth amount is not zero and token minted is not CETH!\n");
|
|
msg->result = ETH_PLUGIN_RESULT_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
if (i == NUM_COMPOUND_SELECTORS) {
|
|
PRINTF("Unknown selector %.*H\n", SELECTOR_SIZE, msg->selector);
|
|
msg->result = ETH_PLUGIN_RESULT_ERROR;
|
|
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);
|
|
msg->result = ETH_PLUGIN_RESULT_ERROR;
|
|
break;
|
|
}
|
|
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,
|
|
msg->pluginSharedRO->txContent->value.length);
|
|
}
|
|
PRINTF("compound plugin inititialized\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");
|
|
msg->result = ETH_PLUGIN_RESULT_ERROR;
|
|
break;
|
|
}
|
|
} else {
|
|
PRINTF("CETH contract expects no parameters\n");
|
|
msg->result = ETH_PLUGIN_RESULT_ERROR;
|
|
}
|
|
} 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_INFO: {
|
|
ethPluginProvideInfo_t *msg = (ethPluginProvideInfo_t *) parameters;
|
|
compound_parameters_t *context = (compound_parameters_t *) msg->pluginContext;
|
|
PRINTF("compound plugin provide token: %d\n", (msg->item1 != NULL));
|
|
if (msg->item1 != NULL) {
|
|
strlcpy(context->ticker_1, msg->item1->token.ticker, MAX_TICKER_LEN);
|
|
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->item1->token.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;
|
|
strlcpy(msg->name, "Type", msg->nameLength);
|
|
switch (context->selectorIndex) {
|
|
case COMPOUND_REDEEM_UNDERLYING:
|
|
case COMPOUND_REDEEM:
|
|
strlcpy(msg->version, "Redeem", msg->versionLength);
|
|
break;
|
|
|
|
case COMPOUND_MINT:
|
|
case CETH_MINT:
|
|
strlcpy(msg->version, "Lend", msg->versionLength);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
strlcat(msg->version, " Assets", msg->versionLength);
|
|
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: {
|
|
strlcpy(msg->title, "Amount", msg->titleLength);
|
|
char *ticker_ptr = 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:
|
|
strlcpy(msg->title, "Contract", msg->titleLength);
|
|
strlcpy(msg->msg, "Compound ", msg->msgLength);
|
|
strlcat(msg->msg,
|
|
context->ticker_1 + 1,
|
|
msg->msgLength); // 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);
|
|
}
|
|
}
|