Add support for ERC-721 and ERC-1155 (v3) (#218)
* 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>
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
#define INS_GET_ETH2_PUBLIC_KEY 0x0E
|
||||
#define INS_SET_ETH2_WITHDRAWAL_INDEX 0x10
|
||||
#define INS_SET_EXTERNAL_PLUGIN 0x12
|
||||
#define INS_PROVIDE_NFT_INFORMATION 0x14
|
||||
#define INS_SET_PLUGIN 0x16
|
||||
#define P1_CONFIRM 0x01
|
||||
#define P1_NON_CONFIRM 0x00
|
||||
#define P2_NO_CHAINCODE 0x00
|
||||
@@ -64,6 +66,12 @@ void handleProvideErc20TokenInformation(uint8_t p1,
|
||||
uint16_t dataLength,
|
||||
unsigned int *flags,
|
||||
unsigned int *tx);
|
||||
void handleProvideNFTInformation(uint8_t p1,
|
||||
uint8_t p2,
|
||||
uint8_t *dataBuffer,
|
||||
uint16_t dataLength,
|
||||
unsigned int *flags,
|
||||
unsigned int *tx);
|
||||
void handleSign(uint8_t p1,
|
||||
uint8_t p2,
|
||||
uint8_t *dataBuffer,
|
||||
@@ -96,6 +104,13 @@ void handleSetExternalPlugin(uint8_t p1,
|
||||
unsigned int *flags,
|
||||
unsigned int *tx);
|
||||
|
||||
void handleSetPlugin(uint8_t p1,
|
||||
uint8_t p2,
|
||||
uint8_t *workBuffer,
|
||||
uint16_t dataLength,
|
||||
unsigned int *flags,
|
||||
unsigned int *tx);
|
||||
|
||||
#ifdef HAVE_ETH2
|
||||
|
||||
void handleGetEth2PublicKey(uint8_t p1,
|
||||
|
||||
@@ -22,8 +22,8 @@ void eth_plugin_prepare_finalize(ethPluginFinalize_t *finalize) {
|
||||
memset((uint8_t *) finalize, 0, sizeof(ethPluginFinalize_t));
|
||||
}
|
||||
|
||||
void eth_plugin_prepare_provide_token(ethPluginProvideToken_t *provideToken) {
|
||||
memset((uint8_t *) provideToken, 0, sizeof(ethPluginProvideToken_t));
|
||||
void eth_plugin_prepare_provide_info(ethPluginProvideInfo_t *provideToken) {
|
||||
memset((uint8_t *) provideToken, 0, sizeof(ethPluginProvideInfo_t));
|
||||
}
|
||||
|
||||
void eth_plugin_prepare_query_contract_ID(ethQueryContractID_t *queryContractID,
|
||||
@@ -45,6 +45,23 @@ void eth_plugin_prepare_query_contract_UI(ethQueryContractUI_t *queryContractUI,
|
||||
char *msg,
|
||||
uint32_t msgLength) {
|
||||
memset((uint8_t *) queryContractUI, 0, sizeof(ethQueryContractUI_t));
|
||||
|
||||
// If no extra information was found, set the pointer to NULL
|
||||
if (allzeroes(&tmpCtx.transactionContext.extraInfo[1], sizeof(union extraInfo_t))) {
|
||||
queryContractUI->item1 = NULL;
|
||||
} else {
|
||||
queryContractUI->item1 = &tmpCtx.transactionContext.extraInfo[1];
|
||||
}
|
||||
|
||||
// If no extra information was found, set the pointer to NULL
|
||||
if (allzeroes(&tmpCtx.transactionContext.extraInfo[0], sizeof(union extraInfo_t))) {
|
||||
queryContractUI->item2 = NULL;
|
||||
} else {
|
||||
queryContractUI->item2 = &tmpCtx.transactionContext.extraInfo[0];
|
||||
}
|
||||
|
||||
strlcpy(queryContractUI->network_ticker, get_network_ticker(), MAX_TICKER_LEN);
|
||||
|
||||
queryContractUI->screenIndex = screenIndex;
|
||||
strlcpy(queryContractUI->network_ticker,
|
||||
get_network_ticker(),
|
||||
@@ -62,52 +79,66 @@ eth_plugin_result_t eth_plugin_perform_init(uint8_t *contractAddress,
|
||||
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
|
||||
PRINTF("Selector %.*H\n", 4, init->selector);
|
||||
if (externalPluginIsSet) {
|
||||
// check if the registered external plugin matches the TX contract address / method selector
|
||||
if (memcmp(contractAddress,
|
||||
dataContext.tokenContext.contract_address,
|
||||
sizeof(dataContext.tokenContext.contract_address)) != 0) {
|
||||
PRINTF("Got contract: %.*H\n", ADDRESS_LENGTH, contractAddress);
|
||||
PRINTF("Expected contract: %.*H\n",
|
||||
ADDRESS_LENGTH,
|
||||
dataContext.tokenContext.contract_address);
|
||||
os_sched_exit(0);
|
||||
}
|
||||
if (memcmp(init->selector,
|
||||
dataContext.tokenContext.method_selector,
|
||||
sizeof(dataContext.tokenContext.method_selector)) != 0) {
|
||||
PRINTF("Got selector: %.*H\n", SELECTOR_SIZE, init->selector);
|
||||
PRINTF("Expected selector: %.*H\n",
|
||||
SELECTOR_SIZE,
|
||||
dataContext.tokenContext.method_selector);
|
||||
os_sched_exit(0);
|
||||
}
|
||||
PRINTF("External plugin will be used\n");
|
||||
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_OK;
|
||||
contractAddress = NULL;
|
||||
} else {
|
||||
// Search internal plugin list
|
||||
for (i = 0;; i++) {
|
||||
uint8_t j;
|
||||
selectors = (const uint8_t **) PIC(INTERNAL_ETH_PLUGINS[i].selectors);
|
||||
if (selectors == NULL) {
|
||||
break;
|
||||
switch (pluginType) {
|
||||
case NOT_OLD_INTERNAL:
|
||||
case ERC1155:
|
||||
case ERC721:
|
||||
case EXTERNAL: {
|
||||
// check if the registered external plugin matches the TX contract address / selector
|
||||
if (memcmp(contractAddress,
|
||||
dataContext.tokenContext.contractAddress,
|
||||
sizeof(dataContext.tokenContext.contractAddress)) != 0) {
|
||||
PRINTF("Got contract: %.*H\n", ADDRESS_LENGTH, contractAddress);
|
||||
PRINTF("Expected contract: %.*H\n",
|
||||
ADDRESS_LENGTH,
|
||||
dataContext.tokenContext.contractAddress);
|
||||
os_sched_exit(0);
|
||||
}
|
||||
for (j = 0; ((j < INTERNAL_ETH_PLUGINS[i].num_selectors) && (contractAddress != NULL));
|
||||
j++) {
|
||||
if (memcmp(init->selector, (const void *) PIC(selectors[j]), SELECTOR_SIZE) == 0) {
|
||||
if ((INTERNAL_ETH_PLUGINS[i].availableCheck == NULL) ||
|
||||
((PluginAvailableCheck) PIC(INTERNAL_ETH_PLUGINS[i].availableCheck))()) {
|
||||
strlcpy(dataContext.tokenContext.pluginName,
|
||||
INTERNAL_ETH_PLUGINS[i].alias,
|
||||
PLUGIN_ID_LENGTH);
|
||||
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_OK;
|
||||
contractAddress = NULL;
|
||||
break;
|
||||
if (memcmp(init->selector,
|
||||
dataContext.tokenContext.methodSelector,
|
||||
sizeof(dataContext.tokenContext.methodSelector)) != 0) {
|
||||
PRINTF("Got selector: %.*H\n", SELECTOR_SIZE, init->selector);
|
||||
PRINTF("Expected selector: %.*H\n",
|
||||
SELECTOR_SIZE,
|
||||
dataContext.tokenContext.methodSelector);
|
||||
os_sched_exit(0);
|
||||
}
|
||||
PRINTF("Plugin will be used\n");
|
||||
// TODO: Add check for chainid.
|
||||
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_OK;
|
||||
contractAddress = NULL;
|
||||
} break;
|
||||
case OLD_INTERNAL: {
|
||||
// Search internal plugin list
|
||||
for (i = 0;; i++) {
|
||||
uint8_t j;
|
||||
selectors = (const uint8_t **) PIC(INTERNAL_ETH_PLUGINS[i].selectors);
|
||||
if (selectors == NULL) {
|
||||
break;
|
||||
}
|
||||
for (j = 0;
|
||||
((j < INTERNAL_ETH_PLUGINS[i].num_selectors) && (contractAddress != NULL));
|
||||
j++) {
|
||||
if (memcmp(init->selector, (const void *) PIC(selectors[j]), SELECTOR_SIZE) ==
|
||||
0) {
|
||||
if ((INTERNAL_ETH_PLUGINS[i].availableCheck == NULL) ||
|
||||
((PluginAvailableCheck) PIC(
|
||||
INTERNAL_ETH_PLUGINS[i].availableCheck))()) {
|
||||
strlcpy(dataContext.tokenContext.pluginName,
|
||||
INTERNAL_ETH_PLUGINS[i].alias,
|
||||
PLUGIN_ID_LENGTH);
|
||||
dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_OK;
|
||||
contractAddress = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
PRINTF("Unsupported pluginType %d\n", pluginType);
|
||||
os_sched_exit(0);
|
||||
break;
|
||||
}
|
||||
|
||||
// Do not handle a plugin if running in swap mode
|
||||
@@ -140,7 +171,6 @@ eth_plugin_result_t eth_plugin_call(int method, void *parameter) {
|
||||
ethPluginSharedRO_t pluginRO;
|
||||
char *alias;
|
||||
uint8_t i;
|
||||
uint8_t internalPlugin = 0;
|
||||
|
||||
pluginRW.sha3 = &global_sha3;
|
||||
pluginRO.txContent = &tmpContent.txContent;
|
||||
@@ -183,12 +213,12 @@ eth_plugin_result_t eth_plugin_call(int method, void *parameter) {
|
||||
((ethPluginFinalize_t *) parameter)->pluginContext =
|
||||
(uint8_t *) &dataContext.tokenContext.pluginContext;
|
||||
break;
|
||||
case ETH_PLUGIN_PROVIDE_TOKEN:
|
||||
PRINTF("-- PLUGIN PROVIDE TOKEN --\n");
|
||||
((ethPluginProvideToken_t *) parameter)->result = ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
((ethPluginProvideToken_t *) parameter)->pluginSharedRW = &pluginRW;
|
||||
((ethPluginProvideToken_t *) parameter)->pluginSharedRO = &pluginRO;
|
||||
((ethPluginProvideToken_t *) parameter)->pluginContext =
|
||||
case ETH_PLUGIN_PROVIDE_INFO:
|
||||
PRINTF("-- PLUGIN PROVIDE INFO --\n");
|
||||
((ethPluginProvideInfo_t *) parameter)->result = ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
((ethPluginProvideInfo_t *) parameter)->pluginSharedRW = &pluginRW;
|
||||
((ethPluginProvideInfo_t *) parameter)->pluginSharedRO = &pluginRO;
|
||||
((ethPluginProvideInfo_t *) parameter)->pluginContext =
|
||||
(uint8_t *) &dataContext.tokenContext.pluginContext;
|
||||
break;
|
||||
case ETH_PLUGIN_QUERY_CONTRACT_ID:
|
||||
@@ -211,35 +241,52 @@ eth_plugin_result_t eth_plugin_call(int method, void *parameter) {
|
||||
return ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
}
|
||||
|
||||
// Perform the call
|
||||
|
||||
for (i = 0;; i++) {
|
||||
if (INTERNAL_ETH_PLUGINS[i].alias[0] == 0) {
|
||||
switch (pluginType) {
|
||||
case NOT_OLD_INTERNAL:
|
||||
break;
|
||||
case EXTERNAL: {
|
||||
uint32_t params[3];
|
||||
params[0] = (uint32_t) alias;
|
||||
params[1] = method;
|
||||
params[2] = (uint32_t) parameter;
|
||||
BEGIN_TRY {
|
||||
TRY {
|
||||
os_lib_call(params);
|
||||
}
|
||||
CATCH_OTHER(e) {
|
||||
PRINTF("Plugin call exception for %s\n", alias);
|
||||
}
|
||||
FINALLY {
|
||||
}
|
||||
}
|
||||
END_TRY;
|
||||
break;
|
||||
}
|
||||
if (strcmp(alias, INTERNAL_ETH_PLUGINS[i].alias) == 0) {
|
||||
internalPlugin = 1;
|
||||
((PluginCall) PIC(INTERNAL_ETH_PLUGINS[i].impl))(method, parameter);
|
||||
case ERC721: {
|
||||
erc721_plugin_call(method, parameter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!internalPlugin) {
|
||||
uint32_t params[3];
|
||||
params[0] = (uint32_t) alias;
|
||||
params[1] = method;
|
||||
params[2] = (uint32_t) parameter;
|
||||
BEGIN_TRY {
|
||||
TRY {
|
||||
os_lib_call(params);
|
||||
}
|
||||
CATCH_OTHER(e) {
|
||||
PRINTF("Plugin call exception for %s\n", alias);
|
||||
}
|
||||
FINALLY {
|
||||
}
|
||||
case ERC1155: {
|
||||
erc1155_plugin_call(method, parameter);
|
||||
break;
|
||||
}
|
||||
case OLD_INTERNAL: {
|
||||
// Perform the call
|
||||
for (i = 0;; i++) {
|
||||
if (INTERNAL_ETH_PLUGINS[i].alias[0] == 0) {
|
||||
break;
|
||||
}
|
||||
if (strcmp(alias, INTERNAL_ETH_PLUGINS[i].alias) == 0) {
|
||||
((PluginCall) PIC(INTERNAL_ETH_PLUGINS[i].impl))(method, parameter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
PRINTF("Error with pluginType: %d\n", pluginType);
|
||||
return ETH_PLUGIN_RESULT_ERROR;
|
||||
}
|
||||
END_TRY;
|
||||
}
|
||||
|
||||
// Check the call result
|
||||
@@ -277,9 +324,9 @@ eth_plugin_result_t eth_plugin_call(int method, void *parameter) {
|
||||
return ETH_PLUGIN_RESULT_UNAVAILABLE;
|
||||
}
|
||||
break;
|
||||
case ETH_PLUGIN_PROVIDE_TOKEN:
|
||||
PRINTF("RESULT: %d\n", ((ethPluginProvideToken_t *) parameter)->result);
|
||||
switch (((ethPluginProvideToken_t *) parameter)->result) {
|
||||
case ETH_PLUGIN_PROVIDE_INFO:
|
||||
PRINTF("RESULT: %d\n", ((ethPluginProvideInfo_t *) parameter)->result);
|
||||
switch (((ethPluginProvideInfo_t *) parameter)->result) {
|
||||
case ETH_PLUGIN_RESULT_OK:
|
||||
case ETH_PLUGIN_RESULT_FALLBACK:
|
||||
break;
|
||||
|
||||
@@ -7,7 +7,7 @@ void eth_plugin_prepare_provide_parameter(ethPluginProvideParameter_t *providePa
|
||||
uint8_t *parameter,
|
||||
uint32_t parameterOffset);
|
||||
void eth_plugin_prepare_finalize(ethPluginFinalize_t *finalize);
|
||||
void eth_plugin_prepare_provide_token(ethPluginProvideToken_t *provideToken);
|
||||
void eth_plugin_prepare_provide_info(ethPluginProvideInfo_t *provideToken);
|
||||
void eth_plugin_prepare_query_contract_ID(ethQueryContractID_t *queryContractID,
|
||||
char *name,
|
||||
uint32_t nameLength,
|
||||
@@ -24,7 +24,6 @@ eth_plugin_result_t eth_plugin_perform_init(uint8_t *contractAddress,
|
||||
ethPluginInitContract_t *init);
|
||||
// NULL for cached address, or base contract address
|
||||
eth_plugin_result_t eth_plugin_call(int method, void *parameter);
|
||||
int compound_plugin_call(uint8_t *contractAddress, int method, void *parameter);
|
||||
|
||||
void plugin_ui_start(void);
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
#include "cx.h"
|
||||
#include "ethUstream.h"
|
||||
#include "tokens.h"
|
||||
|
||||
#define PLUGIN_ID_LENGTH 30
|
||||
#include "shared_context.h"
|
||||
|
||||
// Interface version. To be updated everytime we introduce breaking changes to the plugin interface.
|
||||
typedef enum {
|
||||
ETH_PLUGIN_INTERFACE_VERSION_1 = 1, // Version 1
|
||||
ETH_PLUGIN_INTERFACE_VERSION_1 = 1,
|
||||
ETH_PLUGIN_INTERFACE_VERSION_2 = 2,
|
||||
ETH_PLUGIN_INTERFACE_VERSION_LATEST = 3,
|
||||
ETH_PLUGIN_INTERFACE_VERSION_3 = 3,
|
||||
ETH_PLUGIN_INTERFACE_VERSION_LATEST = 4,
|
||||
} eth_plugin_interface_version_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -21,7 +21,7 @@ typedef enum {
|
||||
ETH_PLUGIN_INIT_CONTRACT = 0x0101,
|
||||
ETH_PLUGIN_PROVIDE_PARAMETER = 0x0102,
|
||||
ETH_PLUGIN_FINALIZE = 0x0103,
|
||||
ETH_PLUGIN_PROVIDE_TOKEN = 0x0104,
|
||||
ETH_PLUGIN_PROVIDE_INFO = 0x0104,
|
||||
ETH_PLUGIN_QUERY_CONTRACT_ID = 0x0105,
|
||||
ETH_PLUGIN_QUERY_CONTRACT_UI = 0x0106,
|
||||
ETH_PLUGIN_CHECK_PRESENCE = 0x01FF
|
||||
@@ -126,20 +126,20 @@ typedef struct ethPluginFinalize_t {
|
||||
|
||||
// Provide token
|
||||
|
||||
typedef struct ethPluginProvideToken_t {
|
||||
typedef struct ethPluginProvideInfo_t {
|
||||
ethPluginSharedRW_t *pluginSharedRW;
|
||||
ethPluginSharedRO_t *pluginSharedRO;
|
||||
uint8_t *pluginContext;
|
||||
|
||||
tokenDefinition_t *token1; // set by the ETH application, to be saved by the plugin
|
||||
tokenDefinition_t *token2;
|
||||
union extraInfo_t *item1; // set by the ETH application, to be saved by the plugin
|
||||
union extraInfo_t *item2;
|
||||
|
||||
uint8_t additionalScreens; // Used by the plugin if it needs to display additional screens
|
||||
// based on the information received from the token definitions.
|
||||
|
||||
uint8_t result;
|
||||
|
||||
} ethPluginProvideToken_t;
|
||||
} ethPluginProvideInfo_t;
|
||||
|
||||
// Query Contract name and version
|
||||
|
||||
@@ -164,9 +164,11 @@ typedef struct ethQueryContractID_t {
|
||||
typedef struct ethQueryContractUI_t {
|
||||
ethPluginSharedRW_t *pluginSharedRW;
|
||||
ethPluginSharedRO_t *pluginSharedRO;
|
||||
union extraInfo_t *item1;
|
||||
union extraInfo_t *item2;
|
||||
char network_ticker[MAX_TICKER_LEN];
|
||||
uint8_t *pluginContext;
|
||||
uint8_t screenIndex;
|
||||
char network_ticker[MAX_TICKER_LEN];
|
||||
|
||||
char *title;
|
||||
size_t titleLength;
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
#include "eth_plugin_internal.h"
|
||||
|
||||
bool erc20_plugin_available_check(void);
|
||||
bool erc721_plugin_available_check(void);
|
||||
|
||||
void erc20_plugin_call(int message, void* parameters);
|
||||
void erc721_plugin_call(int message, void* parameters);
|
||||
void compound_plugin_call(int message, void* parameters);
|
||||
|
||||
void copy_address(uint8_t* dst, uint8_t* parameter, uint8_t dst_size) {
|
||||
uint8_t copy_size = MIN(dst_size, ADDRESS_LENGTH);
|
||||
memmove(dst, parameter + PARAMETER_LENGTH - copy_size, copy_size);
|
||||
}
|
||||
|
||||
void copy_parameter(uint8_t* dst, uint8_t* parameter, uint8_t dst_size) {
|
||||
uint8_t copy_size = MIN(dst_size, PARAMETER_LENGTH);
|
||||
memmove(dst, parameter, copy_size);
|
||||
}
|
||||
|
||||
#ifdef HAVE_STARKWARE
|
||||
void starkware_plugin_call(int message, void* parameters);
|
||||
#endif
|
||||
@@ -19,10 +28,6 @@ static const uint8_t ERC20_APPROVE_SELECTOR[SELECTOR_SIZE] = {0x09, 0x5e, 0xa7,
|
||||
const uint8_t* const ERC20_SELECTORS[NUM_ERC20_SELECTORS] = {ERC20_TRANSFER_SELECTOR,
|
||||
ERC20_APPROVE_SELECTOR};
|
||||
|
||||
static const uint8_t ERC721_APPROVE_SELECTOR[SELECTOR_SIZE] = {0x09, 0x5e, 0xa7, 0xb3};
|
||||
|
||||
const uint8_t* const ERC721_SELECTORS[NUM_ERC721_SELECTORS] = {ERC721_APPROVE_SELECTOR};
|
||||
|
||||
static const uint8_t COMPOUND_REDEEM_UNDERLYING_SELECTOR[SELECTOR_SIZE] = {0x85, 0x2a, 0x12, 0xe3};
|
||||
static const uint8_t COMPOUND_REDEEM_SELECTOR[SELECTOR_SIZE] = {0xdb, 0x00, 0x6a, 0x75};
|
||||
static const uint8_t COMPOUND_MINT_SELECTOR[SELECTOR_SIZE] = {0xa0, 0x71, 0x2d, 0x68};
|
||||
@@ -105,12 +110,6 @@ const internalEthPlugin_t INTERNAL_ETH_PLUGINS[] = {
|
||||
"-erc20",
|
||||
erc20_plugin_call},
|
||||
|
||||
{erc721_plugin_available_check,
|
||||
(const uint8_t**) ERC721_SELECTORS,
|
||||
NUM_ERC721_SELECTORS,
|
||||
"-er721",
|
||||
erc721_plugin_call},
|
||||
|
||||
{NULL,
|
||||
(const uint8_t**) COMPOUND_SELECTORS,
|
||||
NUM_COMPOUND_SELECTORS,
|
||||
|
||||
@@ -6,6 +6,13 @@
|
||||
#define PARAMETER_LENGTH 32
|
||||
#define RUN_APPLICATION 1
|
||||
|
||||
void copy_address(uint8_t* dst, uint8_t* parameter, uint8_t dst_size);
|
||||
|
||||
void copy_parameter(uint8_t* dst, uint8_t* parameter, uint8_t dst_size);
|
||||
|
||||
void erc721_plugin_call(int message, void* parameters);
|
||||
void erc1155_plugin_call(int message, void* parameters);
|
||||
|
||||
typedef bool (*PluginAvailableCheck)(void);
|
||||
|
||||
typedef struct internalEthPlugin_t {
|
||||
@@ -19,9 +26,6 @@ typedef struct internalEthPlugin_t {
|
||||
#define NUM_ERC20_SELECTORS 2
|
||||
extern const uint8_t* const ERC20_SELECTORS[NUM_ERC20_SELECTORS];
|
||||
|
||||
#define NUM_ERC721_SELECTORS 1
|
||||
extern const uint8_t* const ERC721_SELECTORS[NUM_ERC721_SELECTORS];
|
||||
|
||||
#define NUM_COMPOUND_SELECTORS 4
|
||||
extern const uint8_t* const COMPOUND_SELECTORS[NUM_COMPOUND_SELECTORS];
|
||||
|
||||
|
||||
54
src/main.c
54
src/main.c
@@ -50,7 +50,7 @@ cx_sha3_t global_sha3;
|
||||
|
||||
uint8_t appState;
|
||||
bool called_from_swap;
|
||||
bool externalPluginIsSet;
|
||||
pluginType_t pluginType;
|
||||
#ifdef HAVE_STARKWARE
|
||||
bool quantumSet;
|
||||
#endif
|
||||
@@ -72,7 +72,7 @@ void reset_app_context() {
|
||||
// PRINTF("!!RESET_APP_CONTEXT\n");
|
||||
appState = APP_STATE_IDLE;
|
||||
called_from_swap = false;
|
||||
externalPluginIsSet = false;
|
||||
pluginType = OLD_INTERNAL;
|
||||
#ifdef HAVE_STARKWARE
|
||||
quantumSet = false;
|
||||
#endif
|
||||
@@ -155,8 +155,8 @@ unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
tokenDefinition_t *getKnownToken(uint8_t *contractAddress) {
|
||||
tokenDefinition_t *currentToken = NULL;
|
||||
extraInfo_t *getKnownToken(uint8_t *contractAddress) {
|
||||
union extraInfo_t *currentItem = NULL;
|
||||
#ifdef HAVE_TOKENS_LIST
|
||||
uint32_t numTokens = 0;
|
||||
uint32_t i;
|
||||
@@ -380,12 +380,22 @@ tokenDefinition_t *getKnownToken(uint8_t *contractAddress) {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (size_t i = 0; i < MAX_TOKEN; i++) {
|
||||
currentToken = &tmpCtx.transactionContext.tokens[i];
|
||||
//
|
||||
for (uint8_t i = 0; i < MAX_ITEMS; i++) {
|
||||
currentItem = (union extraInfo_t *) &tmpCtx.transactionContext.extraInfo[i].token;
|
||||
if (tmpCtx.transactionContext.tokenSet[i] &&
|
||||
(memcmp(currentToken->address, contractAddress, ADDRESS_LENGTH) == 0)) {
|
||||
(memcmp(currentItem->token.address, contractAddress, ADDRESS_LENGTH) == 0)) {
|
||||
PRINTF("Token found at index %d\n", i);
|
||||
return currentToken;
|
||||
return currentItem;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < MAX_ITEMS; i++) {
|
||||
currentItem = (union extraInfo_t *) &tmpCtx.transactionContext.extraInfo[i].token;
|
||||
if (tmpCtx.transactionContext.tokenSet[i] &&
|
||||
(memcmp(currentItem->nft.contractAddress, contractAddress, ADDRESS_LENGTH) == 0)) {
|
||||
PRINTF("Token found at index %d\n", i);
|
||||
return currentItem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,7 +495,7 @@ void handleApdu(unsigned int *flags, unsigned int *tx) {
|
||||
|
||||
switch (G_io_apdu_buffer[OFFSET_INS]) {
|
||||
case INS_GET_PUBLIC_KEY:
|
||||
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_TOKEN);
|
||||
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS);
|
||||
handleGetPublicKey(G_io_apdu_buffer[OFFSET_P1],
|
||||
G_io_apdu_buffer[OFFSET_P2],
|
||||
G_io_apdu_buffer + OFFSET_CDATA,
|
||||
@@ -503,6 +513,15 @@ void handleApdu(unsigned int *flags, unsigned int *tx) {
|
||||
tx);
|
||||
break;
|
||||
|
||||
case INS_PROVIDE_NFT_INFORMATION:
|
||||
handleProvideNFTInformation(G_io_apdu_buffer[OFFSET_P1],
|
||||
G_io_apdu_buffer[OFFSET_P2],
|
||||
G_io_apdu_buffer + OFFSET_CDATA,
|
||||
G_io_apdu_buffer[OFFSET_LC],
|
||||
flags,
|
||||
tx);
|
||||
break;
|
||||
|
||||
case INS_SET_EXTERNAL_PLUGIN:
|
||||
handleSetExternalPlugin(G_io_apdu_buffer[OFFSET_P1],
|
||||
G_io_apdu_buffer[OFFSET_P2],
|
||||
@@ -512,6 +531,15 @@ void handleApdu(unsigned int *flags, unsigned int *tx) {
|
||||
tx);
|
||||
break;
|
||||
|
||||
case INS_SET_PLUGIN:
|
||||
handleSetPlugin(G_io_apdu_buffer[OFFSET_P1],
|
||||
G_io_apdu_buffer[OFFSET_P2],
|
||||
G_io_apdu_buffer + OFFSET_CDATA,
|
||||
G_io_apdu_buffer[OFFSET_LC],
|
||||
flags,
|
||||
tx);
|
||||
break;
|
||||
|
||||
case INS_SIGN:
|
||||
handleSign(G_io_apdu_buffer[OFFSET_P1],
|
||||
G_io_apdu_buffer[OFFSET_P2],
|
||||
@@ -531,7 +559,7 @@ void handleApdu(unsigned int *flags, unsigned int *tx) {
|
||||
break;
|
||||
|
||||
case INS_SIGN_PERSONAL_MESSAGE:
|
||||
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_TOKEN);
|
||||
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS);
|
||||
handleSignPersonalMessage(G_io_apdu_buffer[OFFSET_P1],
|
||||
G_io_apdu_buffer[OFFSET_P2],
|
||||
G_io_apdu_buffer + OFFSET_CDATA,
|
||||
@@ -541,7 +569,7 @@ void handleApdu(unsigned int *flags, unsigned int *tx) {
|
||||
break;
|
||||
|
||||
case INS_SIGN_EIP_712_MESSAGE:
|
||||
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_TOKEN);
|
||||
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS);
|
||||
handleSignEIP712Message(G_io_apdu_buffer[OFFSET_P1],
|
||||
G_io_apdu_buffer[OFFSET_P2],
|
||||
G_io_apdu_buffer + OFFSET_CDATA,
|
||||
@@ -553,7 +581,7 @@ void handleApdu(unsigned int *flags, unsigned int *tx) {
|
||||
#ifdef HAVE_ETH2
|
||||
|
||||
case INS_GET_ETH2_PUBLIC_KEY:
|
||||
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_TOKEN);
|
||||
memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS);
|
||||
handleGetEth2PublicKey(G_io_apdu_buffer[OFFSET_P1],
|
||||
G_io_apdu_buffer[OFFSET_P2],
|
||||
G_io_apdu_buffer + OFFSET_CDATA,
|
||||
@@ -762,7 +790,7 @@ void coin_main(chain_config_t *coin_config) {
|
||||
chainConfig = coin_config;
|
||||
}
|
||||
reset_app_context();
|
||||
tmpCtx.transactionContext.currentTokenIndex = 0;
|
||||
tmpCtx.transactionContext.currentItemIndex = 0;
|
||||
|
||||
for (;;) {
|
||||
UX_INIT();
|
||||
|
||||
14
src/nft.h
Normal file
14
src/nft.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "tokens.h"
|
||||
|
||||
// An `nftInfo_t` must be the same size as a `tokenDefinition_t`. This is because both will be held
|
||||
// in a `extraInfo_t` which is a union of a `nftInfo_t` and a `tokenDefinition_t`. By having both
|
||||
// struct the same size, we know they will be aligned, which facilitates accessing the items.
|
||||
|
||||
// We defined the collection name max length to be the size of a `tokenDefinition_t` and remove the
|
||||
// `ADDRESS_LENGTH` which corresponds to `sizeof(contractAddress`).
|
||||
#define COLLECTION_NAME_MAX_LEN sizeof(tokenDefinition_t) - ADDRESS_LENGTH
|
||||
|
||||
typedef struct nftInfo_t {
|
||||
char collectionName[COLLECTION_NAME_MAX_LEN];
|
||||
char contractAddress[ADDRESS_LENGTH];
|
||||
} nftInfo_t;
|
||||
@@ -14,16 +14,16 @@
|
||||
#include "uint256.h"
|
||||
#include "tokens.h"
|
||||
#include "chainConfig.h"
|
||||
#include "eth_plugin_interface.h"
|
||||
#include "nft.h"
|
||||
|
||||
#define MAX_BIP32_PATH 10
|
||||
|
||||
#define MAX_TOKEN 2
|
||||
|
||||
#define WEI_TO_ETHER 18
|
||||
|
||||
#define SELECTOR_LENGTH 4
|
||||
|
||||
#define PLUGIN_ID_LENGTH 30
|
||||
|
||||
#define N_storage (*(volatile internalStorage_t *) PIC(&N_storage_real))
|
||||
|
||||
typedef struct internalStorage_t {
|
||||
@@ -62,8 +62,8 @@ typedef struct tokenContext_t {
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint8_t contract_address[ADDRESS_LENGTH];
|
||||
uint8_t method_selector[SELECTOR_LENGTH];
|
||||
uint8_t contractAddress[ADDRESS_LENGTH];
|
||||
uint8_t methodSelector[SELECTOR_LENGTH];
|
||||
};
|
||||
uint8_t pluginContext[5 * INT256_LENGTH];
|
||||
};
|
||||
@@ -84,13 +84,18 @@ typedef struct publicKeyContext_t {
|
||||
bool getChaincode;
|
||||
} publicKeyContext_t;
|
||||
|
||||
typedef union extraInfo_t {
|
||||
tokenDefinition_t token;
|
||||
nftInfo_t nft;
|
||||
} extraInfo_t;
|
||||
|
||||
typedef struct transactionContext_t {
|
||||
uint8_t pathLength;
|
||||
uint32_t bip32Path[MAX_BIP32_PATH];
|
||||
uint8_t hash[INT256_LENGTH];
|
||||
tokenDefinition_t tokens[MAX_TOKEN];
|
||||
uint8_t tokenSet[MAX_TOKEN];
|
||||
uint8_t currentTokenIndex;
|
||||
union extraInfo_t extraInfo[MAX_ITEMS];
|
||||
uint8_t tokenSet[MAX_ITEMS];
|
||||
uint8_t currentItemIndex;
|
||||
} transactionContext_t;
|
||||
|
||||
typedef struct messageSigningContext_t {
|
||||
@@ -137,6 +142,7 @@ typedef struct starkContext_t {
|
||||
|
||||
typedef union {
|
||||
tokenContext_t tokenContext;
|
||||
|
||||
#ifdef HAVE_STARKWARE
|
||||
starkContext_t starkContext;
|
||||
#endif
|
||||
@@ -166,7 +172,7 @@ typedef enum {
|
||||
|
||||
typedef struct txStringProperties_t {
|
||||
char fullAddress[43];
|
||||
char fullAmount[67];
|
||||
char fullAmount[79]; // 2^256 is 78 digits long
|
||||
char maxFee[50];
|
||||
char nonce[8]; // 10M tx per account ought to be enough for everybody
|
||||
char network_name[NETWORK_STRING_MAX_SIZE];
|
||||
@@ -196,7 +202,17 @@ extern cx_sha3_t global_sha3;
|
||||
extern const internalStorage_t N_storage_real;
|
||||
|
||||
extern bool called_from_swap;
|
||||
extern bool externalPluginIsSet;
|
||||
|
||||
typedef enum {
|
||||
EXTERNAL, // External plugin, set by setExternalPlugin.
|
||||
ERC721, // Specific ERC721 internal plugin, set by setPlugin.
|
||||
ERC1155, // Specific ERC1155 internal plugin, set by setPlugin
|
||||
OLD_INTERNAL, // Old internal plugin, not set by any command.
|
||||
NOT_OLD_INTERNAL, // Do not treat this tx as an old internal transaction.
|
||||
} pluginType_t;
|
||||
|
||||
extern pluginType_t pluginType;
|
||||
|
||||
extern uint8_t appState;
|
||||
#ifdef HAVE_STARKWARE
|
||||
extern bool quantumSet;
|
||||
|
||||
@@ -68,7 +68,7 @@ void stark_get_amount_string(uint8_t *contractAddress,
|
||||
decimals = WEI_TO_ETHER;
|
||||
PRINTF("stark_get_amount_string - ETH\n");
|
||||
} else {
|
||||
tokenDefinition_t *token = getKnownToken(contractAddress);
|
||||
tokenDefinition_t *token = &getKnownToken(contractAddress)->token;
|
||||
if (token == NULL) { // caught earlier
|
||||
THROW(0x6A80);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "ethUstream.h"
|
||||
|
||||
#define MAX_TICKER_LEN 12 // 10 characters + ' ' + '\0'
|
||||
#define MAX_ITEMS 2
|
||||
|
||||
typedef struct tokenDefinition_t {
|
||||
#ifdef HAVE_CONTRACT_NAME_IN_DESCRIPTOR
|
||||
@@ -29,6 +30,9 @@ typedef struct tokenDefinition_t {
|
||||
#endif
|
||||
uint8_t address[ADDRESS_LENGTH];
|
||||
char ticker[MAX_TICKER_LEN];
|
||||
char nft_pad[20]; // Adding some padding because the `nftInfo_t` is based on the size of a
|
||||
// `tokenDefinition_t`. By adding some padding here we give more space to the
|
||||
// collection name in the `nftInfo_t`. See `nftInfo_t` for more information.
|
||||
uint8_t decimals;
|
||||
} tokenDefinition_t;
|
||||
|
||||
|
||||
@@ -21,4 +21,4 @@ void ui_warning_contract_data(void);
|
||||
void io_seproxyhal_send_status(uint32_t sw);
|
||||
void format_signature_out(const uint8_t *signature);
|
||||
void finalizeParsing(bool direct);
|
||||
tokenDefinition_t *getKnownToken(uint8_t *contractAddress);
|
||||
extraInfo_t *getKnownToken(uint8_t *contractAddress);
|
||||
|
||||
Reference in New Issue
Block a user