From a4905326050e01a8a160e2df610ee748a40a2cf6 Mon Sep 17 00:00:00 2001 From: apaillier-ledger <94451027+apaillier-ledger@users.noreply.github.com> Date: Fri, 19 Nov 2021 11:24:28 +0100 Subject: [PATCH 1/9] Fix compilation warnings (#216) * Fix deprecated os_memmove warning Switched to the standard memmove function * Fix get_network_ticker implicit declaration warning Included the required missing header file * Removed useless execution rights from Makefile * Fix directory name in README --- Makefile | 0 README.md | 2 +- src/eth_plugin_handler.c | 1 + src/main.c | 2 +- 4 files changed, 3 insertions(+), 2 deletions(-) mode change 100755 => 100644 Makefile diff --git a/Makefile b/Makefile old mode 100755 new mode 100644 diff --git a/README.md b/README.md index 417c5bb..1713c74 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ First [install yarn](https://classic.yarnpkg.com/en/docs/install/#debian-stable) Open `tests/build_local_test_elfs.sh` and add your BOLOS SDKs path to `NANOS_SDK` and `NANOX_SDK`. This helper script will build the applications required by the test suite and move them at the right place. ``` -cd test +cd tests ./build_local_test_elfs.sh ``` Then you can install the project by simply running: diff --git a/src/eth_plugin_handler.c b/src/eth_plugin_handler.c index dac067a..b1e19c0 100644 --- a/src/eth_plugin_handler.c +++ b/src/eth_plugin_handler.c @@ -2,6 +2,7 @@ #include "eth_plugin_handler.h" #include "eth_plugin_internal.h" #include "shared_context.h" +#include "network.h" void eth_plugin_prepare_init(ethPluginInitContract_t *init, uint8_t *selector, uint32_t dataSize) { memset((uint8_t *) init, 0, sizeof(ethPluginInitContract_t)); diff --git a/src/main.c b/src/main.c index 11358e7..83621aa 100644 --- a/src/main.c +++ b/src/main.c @@ -411,7 +411,7 @@ void handleGetWalletId(volatile unsigned int *tx) { // pubkey -> sha512 cx_hash_sha512(pub.W, sizeof(pub.W), t, sizeof(t)); // ! cookie ! - os_memmove(G_io_apdu_buffer, t, 64); + memmove(G_io_apdu_buffer, t, 64); *tx = 64; THROW(0x9000); } From fcc3dd6d31b941f9335a57cfd2c549c0c9982853 Mon Sep 17 00:00:00 2001 From: apaillier-ledger <94451027+apaillier-ledger@users.noreply.github.com> Date: Mon, 22 Nov 2021 14:39:36 +0100 Subject: [PATCH 2/9] 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 --- Makefile | 8 +- doc/ethapp.asc | 96 ++++++ doc/ethapp_plugins.asc | 35 ++- ethereum-plugin-sdk | 2 +- src/apdu_constants.h | 15 + src/eth_plugin_handler.c | 203 +++++++----- src/eth_plugin_handler.h | 3 +- src/eth_plugin_interface.h | 22 +- src/eth_plugin_internal.c | 23 +- src/eth_plugin_internal.h | 10 +- src/main.c | 54 +++- src/nft.h | 14 + src/shared_context.h | 36 ++- src/stark_crypto.c | 2 +- src/tokens.h | 4 + src/ui_callbacks.h | 2 +- src_common/ethUstream.c | 10 +- .../cmd_provideTokenInfo.c | 18 +- .../cmd_provideNFTInfo.c | 219 +++++++++++++ .../setExternalPlugin/cmd_setExternalPlugin.c | 22 +- src_features/setPlugin/cmd_setPlugin.c | 294 ++++++++++++++++++ src_features/signTx/cmd_signTx.c | 3 +- src_features/signTx/logic_signTx.c | 32 +- .../cmd_stark_provideQuantum.c | 8 +- src_plugins/compound/compound_plugin.c | 13 +- src_plugins/erc1155/erc1155_plugin.c | 136 ++++++++ src_plugins/erc1155/erc1155_plugin.h | 49 +++ .../erc1155/erc1155_provide_parameters.c | 106 +++++++ src_plugins/erc1155/erc1155_ui.c | 135 ++++++++ src_plugins/erc20/erc20_plugin.c | 14 +- src_plugins/erc721/erc721_plugin.c | 249 ++++++++------- src_plugins/erc721/erc721_plugin.h | 42 +++ .../erc721/erc721_provide_parameters.c | 93 ++++++ src_plugins/erc721/erc721_ui.c | 166 ++++++++++ src_plugins/starkware/starkware_plugin.c | 23 +- tests/build_local_test_elfs.sh | 24 +- .../nanos_erc721_approval_for_all/00000.png | Bin 0 -> 541 bytes .../nanos_erc721_approval_for_all/00001.png | Bin 0 -> 444 bytes .../nanos_erc721_approval_for_all/00002.png | Bin 0 -> 748 bytes .../nanos_erc721_approval_for_all/00003.png | Bin 0 -> 764 bytes .../nanos_erc721_approval_for_all/00004.png | Bin 0 -> 540 bytes .../nanos_erc721_approval_for_all/00005.png | Bin 0 -> 630 bytes .../nanos_erc721_approval_for_all/00006.png | Bin 0 -> 835 bytes .../nanos_erc721_approval_for_all/00007.png | Bin 0 -> 838 bytes .../nanos_erc721_approval_for_all/00008.png | Bin 0 -> 679 bytes .../nanos_erc721_approval_for_all/00009.png | Bin 0 -> 511 bytes .../nanos_erc721_approval_for_all/00010.png | Bin 0 -> 798 bytes .../nanos_erc721_approval_for_all/00011.png | Bin 0 -> 501 bytes .../nanos_erc721_approval_for_all/00012.png | Bin 0 -> 582 bytes .../nanos_erc721_approval_for_all/00013.png | Bin 0 -> 531 bytes .../nanos_erc721_safe_transfer/00000.png | Bin 0 -> 541 bytes .../nanos_erc721_safe_transfer/00001.png | Bin 0 -> 408 bytes .../nanos_erc721_safe_transfer/00002.png | Bin 0 -> 693 bytes .../nanos_erc721_safe_transfer/00003.png | Bin 0 -> 739 bytes .../nanos_erc721_safe_transfer/00004.png | Bin 0 -> 481 bytes .../nanos_erc721_safe_transfer/00005.png | Bin 0 -> 639 bytes .../nanos_erc721_safe_transfer/00006.png | Bin 0 -> 835 bytes .../nanos_erc721_safe_transfer/00007.png | Bin 0 -> 838 bytes .../nanos_erc721_safe_transfer/00008.png | Bin 0 -> 679 bytes .../nanos_erc721_safe_transfer/00009.png | Bin 0 -> 532 bytes .../nanos_erc721_safe_transfer/00010.png | Bin 0 -> 582 bytes .../nanos_erc721_safe_transfer/00011.png | Bin 0 -> 531 bytes .../snapshots/nanos_erc721_transfer/00000.png | Bin 0 -> 541 bytes .../snapshots/nanos_erc721_transfer/00001.png | Bin 0 -> 408 bytes .../snapshots/nanos_erc721_transfer/00002.png | Bin 0 -> 688 bytes .../snapshots/nanos_erc721_transfer/00003.png | Bin 0 -> 698 bytes .../snapshots/nanos_erc721_transfer/00004.png | Bin 0 -> 526 bytes .../snapshots/nanos_erc721_transfer/00005.png | Bin 0 -> 679 bytes .../snapshots/nanos_erc721_transfer/00006.png | Bin 0 -> 840 bytes .../snapshots/nanos_erc721_transfer/00007.png | Bin 0 -> 888 bytes .../snapshots/nanos_erc721_transfer/00008.png | Bin 0 -> 629 bytes .../snapshots/nanos_erc721_transfer/00009.png | Bin 0 -> 511 bytes .../snapshots/nanos_erc721_transfer/00010.png | Bin 0 -> 796 bytes .../snapshots/nanos_erc721_transfer/00011.png | Bin 0 -> 492 bytes .../snapshots/nanos_erc721_transfer/00012.png | Bin 0 -> 582 bytes .../snapshots/nanos_erc721_transfer/00013.png | Bin 0 -> 531 bytes .../nanos_erc721_transfer_with_eth/00000.png | Bin 0 -> 541 bytes .../nanos_erc721_transfer_with_eth/00001.png | Bin 0 -> 408 bytes .../nanos_erc721_transfer_with_eth/00002.png | Bin 0 -> 688 bytes .../nanos_erc721_transfer_with_eth/00003.png | Bin 0 -> 698 bytes .../nanos_erc721_transfer_with_eth/00004.png | Bin 0 -> 526 bytes .../nanos_erc721_transfer_with_eth/00005.png | Bin 0 -> 679 bytes .../nanos_erc721_transfer_with_eth/00006.png | Bin 0 -> 840 bytes .../nanos_erc721_transfer_with_eth/00007.png | Bin 0 -> 888 bytes .../nanos_erc721_transfer_with_eth/00008.png | Bin 0 -> 629 bytes .../nanos_erc721_transfer_with_eth/00009.png | Bin 0 -> 404 bytes .../nanos_erc721_transfer_with_eth/00010.png | Bin 0 -> 511 bytes .../nanos_erc721_transfer_with_eth/00011.png | Bin 0 -> 796 bytes .../nanos_erc721_transfer_with_eth/00012.png | Bin 0 -> 492 bytes .../nanos_erc721_transfer_with_eth/00013.png | Bin 0 -> 582 bytes .../nanos_erc721_transfer_with_eth/00014.png | Bin 0 -> 531 bytes tests/src/erc1155.test .js | 27 ++ tests/src/erc721.test.js | 130 ++++++++ tests/src/test.fixture.js | 33 +- 94 files changed, 2026 insertions(+), 349 deletions(-) create mode 100644 src/nft.h create mode 100644 src_features/provideNFTInformation/cmd_provideNFTInfo.c create mode 100644 src_features/setPlugin/cmd_setPlugin.c create mode 100644 src_plugins/erc1155/erc1155_plugin.c create mode 100644 src_plugins/erc1155/erc1155_plugin.h create mode 100644 src_plugins/erc1155/erc1155_provide_parameters.c create mode 100644 src_plugins/erc1155/erc1155_ui.c create mode 100644 src_plugins/erc721/erc721_plugin.h create mode 100644 src_plugins/erc721/erc721_provide_parameters.c create mode 100644 src_plugins/erc721/erc721_ui.c create mode 100644 tests/snapshots/nanos_erc721_approval_for_all/00000.png create mode 100644 tests/snapshots/nanos_erc721_approval_for_all/00001.png create mode 100644 tests/snapshots/nanos_erc721_approval_for_all/00002.png create mode 100644 tests/snapshots/nanos_erc721_approval_for_all/00003.png create mode 100644 tests/snapshots/nanos_erc721_approval_for_all/00004.png create mode 100644 tests/snapshots/nanos_erc721_approval_for_all/00005.png create mode 100644 tests/snapshots/nanos_erc721_approval_for_all/00006.png create mode 100644 tests/snapshots/nanos_erc721_approval_for_all/00007.png create mode 100644 tests/snapshots/nanos_erc721_approval_for_all/00008.png create mode 100644 tests/snapshots/nanos_erc721_approval_for_all/00009.png create mode 100644 tests/snapshots/nanos_erc721_approval_for_all/00010.png create mode 100644 tests/snapshots/nanos_erc721_approval_for_all/00011.png create mode 100644 tests/snapshots/nanos_erc721_approval_for_all/00012.png create mode 100644 tests/snapshots/nanos_erc721_approval_for_all/00013.png create mode 100644 tests/snapshots/nanos_erc721_safe_transfer/00000.png create mode 100644 tests/snapshots/nanos_erc721_safe_transfer/00001.png create mode 100644 tests/snapshots/nanos_erc721_safe_transfer/00002.png create mode 100644 tests/snapshots/nanos_erc721_safe_transfer/00003.png create mode 100644 tests/snapshots/nanos_erc721_safe_transfer/00004.png create mode 100644 tests/snapshots/nanos_erc721_safe_transfer/00005.png create mode 100644 tests/snapshots/nanos_erc721_safe_transfer/00006.png create mode 100644 tests/snapshots/nanos_erc721_safe_transfer/00007.png create mode 100644 tests/snapshots/nanos_erc721_safe_transfer/00008.png create mode 100644 tests/snapshots/nanos_erc721_safe_transfer/00009.png create mode 100644 tests/snapshots/nanos_erc721_safe_transfer/00010.png create mode 100644 tests/snapshots/nanos_erc721_safe_transfer/00011.png create mode 100644 tests/snapshots/nanos_erc721_transfer/00000.png create mode 100644 tests/snapshots/nanos_erc721_transfer/00001.png create mode 100644 tests/snapshots/nanos_erc721_transfer/00002.png create mode 100644 tests/snapshots/nanos_erc721_transfer/00003.png create mode 100644 tests/snapshots/nanos_erc721_transfer/00004.png create mode 100644 tests/snapshots/nanos_erc721_transfer/00005.png create mode 100644 tests/snapshots/nanos_erc721_transfer/00006.png create mode 100644 tests/snapshots/nanos_erc721_transfer/00007.png create mode 100644 tests/snapshots/nanos_erc721_transfer/00008.png create mode 100644 tests/snapshots/nanos_erc721_transfer/00009.png create mode 100644 tests/snapshots/nanos_erc721_transfer/00010.png create mode 100644 tests/snapshots/nanos_erc721_transfer/00011.png create mode 100644 tests/snapshots/nanos_erc721_transfer/00012.png create mode 100644 tests/snapshots/nanos_erc721_transfer/00013.png create mode 100644 tests/snapshots/nanos_erc721_transfer_with_eth/00000.png create mode 100644 tests/snapshots/nanos_erc721_transfer_with_eth/00001.png create mode 100644 tests/snapshots/nanos_erc721_transfer_with_eth/00002.png create mode 100644 tests/snapshots/nanos_erc721_transfer_with_eth/00003.png create mode 100644 tests/snapshots/nanos_erc721_transfer_with_eth/00004.png create mode 100644 tests/snapshots/nanos_erc721_transfer_with_eth/00005.png create mode 100644 tests/snapshots/nanos_erc721_transfer_with_eth/00006.png create mode 100644 tests/snapshots/nanos_erc721_transfer_with_eth/00007.png create mode 100644 tests/snapshots/nanos_erc721_transfer_with_eth/00008.png create mode 100644 tests/snapshots/nanos_erc721_transfer_with_eth/00009.png create mode 100644 tests/snapshots/nanos_erc721_transfer_with_eth/00010.png create mode 100644 tests/snapshots/nanos_erc721_transfer_with_eth/00011.png create mode 100644 tests/snapshots/nanos_erc721_transfer_with_eth/00012.png create mode 100644 tests/snapshots/nanos_erc721_transfer_with_eth/00013.png create mode 100644 tests/snapshots/nanos_erc721_transfer_with_eth/00014.png create mode 100644 tests/src/erc1155.test .js create mode 100644 tests/src/erc721.test.js diff --git a/Makefile b/Makefile index 8748d1f..db34aab 100644 --- a/Makefile +++ b/Makefile @@ -300,12 +300,18 @@ ifneq ($(ALLOW_DATA),0) DEFINES += HAVE_ALLOW_DATA endif -# Bypass the signature verification for setExternalPlugin and provideERC20TokenInfo calls +# Bypass the signature verification for setExternalPlugin, setPlugin, provideERC20TokenInfo and provideNFTInfo calls BYPASS_SIGNATURES:=0 ifneq ($(BYPASS_SIGNATURES),0) DEFINES += HAVE_BYPASS_SIGNATURES endif +# Enable the NFT testing key +NFT_TESTING_KEY:=0 +ifneq ($(NFT_TESTING_KEY),0) +DEFINES += HAVE_NFT_TESTING_KEY +endif + # Enabling debug PRINTF DEBUG:=0 diff --git a/doc/ethapp.asc b/doc/ethapp.asc index b3c01fc..53007b4 100644 --- a/doc/ethapp.asc +++ b/doc/ethapp.asc @@ -23,6 +23,9 @@ Application version 1.5.0 - 25th of September 2020 ## 1.7.6 - Add SET EXTERNAL PLUGIN +## 1.9.13 + - Add SET PLUGIN + ## About This application describes the APDU messages interface to communicate with the Ethereum application. @@ -240,6 +243,49 @@ signed by the following secp256k1 public key 0482bbf2f34f367b2e5bc21847b6566f21f None +### PROVIDE NFT INFORMATION + +#### Description + +This commands provides a trusted description of an NFT to associate a contract address with a collectionName. + +It shall be run immediately before performing a transaction involving a contract calling this contract address to display the proper nft information to the user if necessary, as marked in GET APP CONFIGURATION flags. + +The signature is computed on + +type || version || len(collectionName) || collectionName || address || chainId || keyId || algorithmId || len(signature) || signature + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| E0 | 14 | 00 | 00 | variable | 00 +|============================================================================================================================== + +'Input data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Type | 1 +| Version | 1 +| Collection Name Length | 1 +| Collection Name | variable +| Address | 20 +| Chain ID | 8 +| KeyID | 1 +| Algorithm ID | 1 +| Signature Length | 1 +| Signature | variable +|============================================================================================================================== + +'Output data' + +None + ### SET EXTERNAL PLUGIN @@ -283,6 +329,56 @@ signed by the following secp256k1 public key 0482bbf2f34f367b2e5bc21847b6566f21f None +### SET PLUGIN + +#### Description + +This commands provides the name of a trusted binding of a plugin with a contract address and a supported method selector. This plugin will be called to interpret contract data in the following transaction signing command. + +It can be used to set both internal and external plugins. + +It shall be run immediately before performing a transaction involving a contract supported by this plugin to display the proper information to the user if necessary. + +The function returns an error sw (0x6984) if the plugin requested is not installed on the device, 0x9000 otherwise. + +The plugin names `ERC20`, `ERC721` and `ERC1155` are reserved. Additional plugin names might be added to this list in the future. + +The signature is computed on + +type || version || len(pluginName) || pluginName || address || selector || chainId || keyId || algorithmId || len(signature) || signature + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| E0 | 16 | 00 | 00 | variable | 00 +|============================================================================================================================== + +'Input data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Type | 1 +| Version | 1 +| Plugin Name Length | 1 +| Plugin Name | variable +| Address | 20 +| Selector | 4 +| Chain ID | 8 +| KeyID | 1 +| Algorithm ID | 1 +| Signature Length | 1 +| Signature | variable +|============================================================================================================================== + +'Output data' + +None + ### GET APP CONFIGURATION #### Description diff --git a/doc/ethapp_plugins.asc b/doc/ethapp_plugins.asc index e1233fa..af89087 100644 --- a/doc/ethapp_plugins.asc +++ b/doc/ethapp_plugins.asc @@ -145,8 +145,8 @@ typedef struct ethPluginFinalize_t { ethPluginSharedRO_t *pluginSharedRO; uint8_t *pluginContext; - uint8_t *tokenLookup1; // set by the plugin if a token should be looked up - uint8_t *tokenLookup2; + uint8_t *itemLookup1; // set by the plugin if a token or an nft should be looked up + uint8_t *itemLookup2; uint8_t *amount; // set an uint256 pointer if uiType is UI_AMOUNT_ADDRESS uint8_t *address; // set to the destination address if uiType is UI_AMOUNT_ADDRESS. Set to the user's address if uiType is UI_TYPE_GENERIC @@ -161,8 +161,8 @@ typedef struct ethPluginFinalize_t { This message is sent when the data field has been fully parsed. The following specific fields can be filled by the plugin : - * tokenLookup1 : the pointer shall be set to a 20 bytes address to look up an ERC 20 token descriptor if needed by the plugin - * tokenLookup2 : the pointer shall be set to a 20 bytes address to look up an ERC 20 token descriptor if needed by the plugin + * itemLookup1 : the pointer shall be set to a 20 bytes address to look up an ERC20 token or NFT if needed by the plugin + * itemLookup2 : the pointer shall be set to a 20 bytes address to look up an ERC20 token or NFT if needed by the plugin * uiType : set to either ETH_UI_TYPE_AMOUNT_ADDRESS for an amount/address UI or ETH_UI_TYPE_GENERIC for a generic UI The following specific fields are filled by the plugin when returning an amount/address UI : @@ -179,32 +179,32 @@ The following return codes are expected, any other will abort the signing proces * ETH_PLUGIN_RESULT_OK : if the plugin can be successfully initialized * ETH_PLUGIN_RESULT_FALLBACK : if the signing logic should fallback to the generic one -### ETH_PLUGIN_PROVIDE_TOKEN +### ETH_PLUGIN_PROVIDE_INFO [source,C] ---- -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 *item1; // set by the ETH application, to be saved by the plugin + union extraInfo *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 additionalScreens; // Used by the plugin if it needs to display additional screens based on the information received. uint8_t result; -} ethPluginProvideToken_t; +} ethPluginProvideInfo_t; ---- -This message is sent if a token lookup was required by the plugin when parsing a finalize message. The following specific fields are filled when the plugin is called : +This message is sent if an information lookup was required by the plugin when parsing a finalize message. The following specific fields are filled when the plugin is called : - * token1 : pointer to a token definition matching tokenLookup1, or NULL if not found - * token2 : pointer to a token definition matching tokenLookup2, or NULL if not found or not requested + * item1 : pointer to an union matching itemLookup1, or NULL if not found + * item2 : pointer to an union matching itemLookup2, or NULL if not found The following return codes are expected, any other will abort the signing process : @@ -233,7 +233,7 @@ typedef struct ethQueryContractID_t { ---- -This message is sent after the parsing finalization and token lookups if requested if a generic UI is used. The following specific fields are provided when the plugin is called : +This message is sent after the parsing finalization and information lookups if requested if a generic UI is used. The following specific fields are provided when the plugin is called : * name : pointer to the name of the plugin, to be filled by the plugin * nameLength : maximum name length @@ -253,6 +253,9 @@ 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 *title; @@ -268,6 +271,10 @@ typedef struct ethQueryContractUI_t { This message is sent when a plugin screen shall be displayed if a generic UI is used. The following specific fields are provided when the plugin is called : + + * item1 : pointer to token / nft information + * item2 : pointer to token / nft information + * network_ticker : string that holds the network ticker * screenIndex : index of the screen to display, starting from 0 * title : pointer to the first line of the screen, to be filled by the plugin * titleLength : maximum title length diff --git a/ethereum-plugin-sdk b/ethereum-plugin-sdk index d672ec1..682e67a 160000 --- a/ethereum-plugin-sdk +++ b/ethereum-plugin-sdk @@ -1 +1 @@ -Subproject commit d672ec1d2aadd3a272760168f58022ca674876e3 +Subproject commit 682e67aac9e368caca9ad4a40fc6c65893871504 diff --git a/src/apdu_constants.h b/src/apdu_constants.h index fd3dff9..f0c149b 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -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, diff --git a/src/eth_plugin_handler.c b/src/eth_plugin_handler.c index b1e19c0..ccfbab8 100644 --- a/src/eth_plugin_handler.c +++ b/src/eth_plugin_handler.c @@ -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; diff --git a/src/eth_plugin_handler.h b/src/eth_plugin_handler.h index 8c86af3..864c15c 100644 --- a/src/eth_plugin_handler.h +++ b/src/eth_plugin_handler.h @@ -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); diff --git a/src/eth_plugin_interface.h b/src/eth_plugin_interface.h index 1887154..512716e 100644 --- a/src/eth_plugin_interface.h +++ b/src/eth_plugin_interface.h @@ -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; diff --git a/src/eth_plugin_internal.c b/src/eth_plugin_internal.c index 1ed45cd..a4789f1 100644 --- a/src/eth_plugin_internal.c +++ b/src/eth_plugin_internal.c @@ -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, diff --git a/src/eth_plugin_internal.h b/src/eth_plugin_internal.h index abce9c6..4f10ec5 100644 --- a/src/eth_plugin_internal.h +++ b/src/eth_plugin_internal.h @@ -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]; diff --git a/src/main.c b/src/main.c index 83621aa..05354b9 100644 --- a/src/main.c +++ b/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(); diff --git a/src/nft.h b/src/nft.h new file mode 100644 index 0000000..f6b2d67 --- /dev/null +++ b/src/nft.h @@ -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; \ No newline at end of file diff --git a/src/shared_context.h b/src/shared_context.h index 09aa7d4..ed15d6c 100644 --- a/src/shared_context.h +++ b/src/shared_context.h @@ -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; diff --git a/src/stark_crypto.c b/src/stark_crypto.c index dc4face..990216a 100644 --- a/src/stark_crypto.c +++ b/src/stark_crypto.c @@ -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); } diff --git a/src/tokens.h b/src/tokens.h index 45c7460..13e98e7 100644 --- a/src/tokens.h +++ b/src/tokens.h @@ -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; diff --git a/src/ui_callbacks.h b/src/ui_callbacks.h index 8b0a15a..e7e78ab 100644 --- a/src/ui_callbacks.h +++ b/src/ui_callbacks.h @@ -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); diff --git a/src_common/ethUstream.c b/src_common/ethUstream.c index a706e6e..7f6bc65 100644 --- a/src_common/ethUstream.c +++ b/src_common/ethUstream.c @@ -254,6 +254,7 @@ static void processTo(txContext_t *context) { } static void processData(txContext_t *context) { + PRINTF("PROCESS DATA\n"); if (context->currentFieldIsList) { PRINTF("Invalid type for RLP_DATA\n"); THROW(EXCEPTION); @@ -268,6 +269,7 @@ static void processData(txContext_t *context) { copyTxData(context, NULL, copySize); } if (context->currentFieldPos == context->currentFieldLength) { + PRINTF("incrementing field\n"); context->currentField++; context->processingField = false; } @@ -506,6 +508,7 @@ static parserStatus_e processTxInternal(txContext_t *context) { customStatus_e customStatus = CUSTOM_NOT_HANDLED; // EIP 155 style transaction if (PARSING_IS_DONE(context)) { + PRINTF("parsing is done\n"); return USTREAM_FINISHED; } // Old style transaction (pre EIP-155). Transations could just skip `v,r,s` so we needed to @@ -518,9 +521,11 @@ static parserStatus_e processTxInternal(txContext_t *context) { if ((context->txType == LEGACY && context->currentField == LEGACY_RLP_V) && (context->commandLength == 0)) { context->content->vLength = 0; + PRINTF("finished\n"); return USTREAM_FINISHED; } if (context->commandLength == 0) { + PRINTF("Command length done\n"); return USTREAM_PROCESSING; } if (!context->processingField) { @@ -531,6 +536,7 @@ static parserStatus_e processTxInternal(txContext_t *context) { } if (context->customProcessor != NULL) { customStatus = context->customProcessor(context); + PRINTF("After customprocessor\n"); switch (customStatus) { case CUSTOM_NOT_HANDLED: case CUSTOM_HANDLED: @@ -571,11 +577,12 @@ static parserStatus_e processTxInternal(txContext_t *context) { break; } default: - PRINTF("Transaction type %u is not supported\n", context->txType); + PRINTF("Transaction type %d is not supported\n", context->txType); return USTREAM_FAULT; } } } + PRINTF("end of here\n"); } parserStatus_e processTx(txContext_t *context, @@ -589,6 +596,7 @@ parserStatus_e processTx(txContext_t *context, context->commandLength = length; context->processingFlags = processingFlags; result = processTxInternal(context); + PRINTF("result: %d\n"); } CATCH_OTHER(e) { result = USTREAM_FAULT; diff --git a/src_features/provideErc20TokenInformation/cmd_provideTokenInfo.c b/src_features/provideErc20TokenInformation/cmd_provideTokenInfo.c index 94ddebc..4f09b1f 100644 --- a/src_features/provideErc20TokenInformation/cmd_provideTokenInfo.c +++ b/src_features/provideErc20TokenInformation/cmd_provideTokenInfo.c @@ -23,10 +23,10 @@ void handleProvideErc20TokenInformation(uint8_t p1, cx_sha256_init(&sha256); - tmpCtx.transactionContext.currentTokenIndex = - (tmpCtx.transactionContext.currentTokenIndex + 1) % MAX_TOKEN; + tmpCtx.transactionContext.currentItemIndex = + (tmpCtx.transactionContext.currentItemIndex + 1) % MAX_ITEMS; tokenDefinition_t *token = - &tmpCtx.transactionContext.tokens[tmpCtx.transactionContext.currentTokenIndex]; + &tmpCtx.transactionContext.tokens[tmpCtx.transactionContext.currentItemIndex]; if (dataLength < 1) { THROW(0x6A80); @@ -93,7 +93,7 @@ void handleProvideErc20TokenInformation(uint8_t p1, THROW(0x6A80); #endif } - tmpCtx.transactionContext.tokenSet[tmpCtx.transactionContext.currentTokenIndex] = 1; + tmpCtx.transactionContext.tokenSet[tmpCtx.transactionContext.currentItemIndex] = 1; THROW(0x9000); } @@ -114,12 +114,12 @@ void handleProvideErc20TokenInformation(uint8_t p1, uint8_t hash[INT256_LENGTH]; cx_ecfp_public_key_t tokenKey; - tmpCtx.transactionContext.currentTokenIndex = - (tmpCtx.transactionContext.currentTokenIndex + 1) % MAX_TOKEN; + tmpCtx.transactionContext.currentItemIndex = + (tmpCtx.transactionContext.currentItemIndex + 1) % MAX_ITEMS; tokenDefinition_t *token = - &tmpCtx.transactionContext.tokens[tmpCtx.transactionContext.currentTokenIndex]; + &tmpCtx.transactionContext.extraInfo[tmpCtx.transactionContext.currentItemIndex].token; - PRINTF("Provisioning currentTokenIndex %d\n", tmpCtx.transactionContext.currentTokenIndex); + PRINTF("Provisioning currentItemIndex %d\n", tmpCtx.transactionContext.currentItemIndex); if (dataLength < 1) { THROW(0x6A80); @@ -204,7 +204,7 @@ void handleProvideErc20TokenInformation(uint8_t p1, } #endif - tmpCtx.transactionContext.tokenSet[tmpCtx.transactionContext.currentTokenIndex] = 1; + tmpCtx.transactionContext.tokenSet[tmpCtx.transactionContext.currentItemIndex] = 1; THROW(0x9000); } diff --git a/src_features/provideNFTInformation/cmd_provideNFTInfo.c b/src_features/provideNFTInformation/cmd_provideNFTInfo.c new file mode 100644 index 0000000..692b551 --- /dev/null +++ b/src_features/provideNFTInformation/cmd_provideNFTInfo.c @@ -0,0 +1,219 @@ +#include "shared_context.h" +#include "apdu_constants.h" +#include "ui_flow.h" +#include "tokens.h" +#include "utils.h" + +#define TYPE_SIZE 1 +#define VERSION_SIZE 1 +#define NAME_LENGTH_SIZE 1 +#define HEADER_SIZE TYPE_SIZE + VERSION_SIZE + NAME_LENGTH_SIZE + +#define CHAIN_ID_SIZE 8 +#define KEY_ID_SIZE 1 +#define ALGORITHM_ID_SIZE 1 +#define SIGNATURE_LENGTH_SIZE 1 +#define MIN_DER_SIG_SIZE 67 +#define MAX_DER_SIG_SIZE 72 + +#define TESTING_KEY 0 +#define NFT_METADATA_KEY_1 1 + +#define ALGORITHM_ID_1 1 + +#define TYPE_1 1 + +#define VERSION_1 1 + +#ifdef HAVE_NFT_TESTING_KEY +static const uint8_t LEDGER_NFT_PUBLIC_KEY[] = { + 0x04, 0xf5, 0x70, 0x0c, 0xa1, 0xe8, 0x74, 0x24, 0xc7, 0xc7, 0xd1, 0x19, 0xe7, + 0xe3, 0xc1, 0x89, 0xb1, 0x62, 0x50, 0x94, 0xdb, 0x6e, 0xa0, 0x40, 0x87, 0xc8, + 0x30, 0x00, 0x7d, 0x0b, 0x46, 0x9a, 0x53, 0x11, 0xee, 0x6a, 0x1a, 0xcd, 0x1d, + 0xa5, 0xaa, 0xb0, 0xf5, 0xc6, 0xdf, 0x13, 0x15, 0x8d, 0x28, 0xcc, 0x12, 0xd1, + 0xdd, 0xa6, 0xec, 0xe9, 0x46, 0xb8, 0x9d, 0x5c, 0x05, 0x49, 0x92, 0x59, 0xc4}; + +#else +static const uint8_t LEDGER_NFT_PUBLIC_KEY[] = {}; +#endif + +typedef bool verificationAlgo(const cx_ecfp_public_key_t *, + int, + cx_md_t, + const unsigned char *, + unsigned int, + unsigned char *, + unsigned int); + +void handleProvideNFTInformation(uint8_t p1, + uint8_t p2, + uint8_t *workBuffer, + uint16_t dataLength, + unsigned int *flags, + unsigned int *tx) { + UNUSED(p1); + UNUSED(p2); + UNUSED(tx); + UNUSED(flags); + uint8_t hash[INT256_LENGTH]; + cx_ecfp_public_key_t nftKey; + PRINTF("In handle provide NFTInformation"); + + tmpCtx.transactionContext.currentItemIndex = + (tmpCtx.transactionContext.currentItemIndex + 1) % MAX_ITEMS; + nftInfo_t *nft = + &tmpCtx.transactionContext.extraInfo[tmpCtx.transactionContext.currentItemIndex].nft; + + PRINTF("Provisioning currentItemIndex %d\n", tmpCtx.transactionContext.currentItemIndex); + + uint8_t offset = 0; + + if (dataLength <= HEADER_SIZE) { + PRINTF("Data too small for headers: expected at least %d, got %d\n", + HEADER_SIZE, + dataLength); + THROW(0x6A80); + } + + uint8_t type = workBuffer[offset]; + switch (type) { + case TYPE_1: + break; + default: + PRINTF("Unsupported type %d\n", type); + THROW(0x6a80); + break; + } + offset += TYPE_SIZE; + + uint8_t version = workBuffer[offset]; + switch (version) { + case VERSION_1: + break; + default: + PRINTF("Unsupported version %d\n", version); + THROW(0x6a80); + break; + } + offset += VERSION_SIZE; + + uint8_t collectionNameLength = workBuffer[offset]; + offset += NAME_LENGTH_SIZE; + + // Size of the payload (everything except the signature) + uint8_t payloadSize = HEADER_SIZE + collectionNameLength + ADDRESS_LENGTH + CHAIN_ID_SIZE + + KEY_ID_SIZE + ALGORITHM_ID_SIZE; + if (dataLength < payloadSize) { + PRINTF("Data too small for payload: expected at least %d, got %d\n", + payloadSize, + dataLength); + THROW(0x6A80); + } + + if (collectionNameLength + 1 > sizeof(nft->collectionName)) { + PRINTF("CollectionName too big: expected max %d, got %d\n", + sizeof(nft->collectionName), + collectionNameLength + 1); + THROW(0x6A80); + } + + // Safe because we've checked the size before. + memcpy(nft->collectionName, workBuffer + offset, collectionNameLength); + nft->collectionName[collectionNameLength] = '\0'; + + PRINTF("Length: %d\n", collectionNameLength); + PRINTF("CollectionName: %s\n", nft->collectionName); + offset += collectionNameLength; + + memcpy(nft->contractAddress, workBuffer + offset, ADDRESS_LENGTH); + PRINTF("Address: %.*H\n", ADDRESS_LENGTH, workBuffer + offset); + offset += ADDRESS_LENGTH; + + // TODO: store chainID and assert that tx is using the same chainid. + // uint64_t chainid = u64_from_BE(workBuffer + offset, CHAIN_ID_SIZE); + // PRINTF("ChainID: %.*H\n", sizeof(chainid), &chainid); + offset += CHAIN_ID_SIZE; + + uint8_t keyId = workBuffer[offset]; + uint8_t *rawKey; + uint8_t rawKeyLen; + + PRINTF("KeyID: %d\n", keyId); + switch (keyId) { +#ifdef HAVE_NFT_TESTING_KEY + case TESTING_KEY: +#endif + case NFT_METADATA_KEY_1: + rawKey = (uint8_t *) LEDGER_NFT_PUBLIC_KEY; + rawKeyLen = sizeof(LEDGER_NFT_PUBLIC_KEY); + break; + default: + PRINTF("KeyID %d not supported\n", keyId); + THROW(0x6A80); + break; + } + PRINTF("RawKey: %.*H\n", rawKeyLen, rawKey); + offset += KEY_ID_SIZE; + + uint8_t algorithmId = workBuffer[offset]; + PRINTF("Algorithm: %d\n", algorithmId); + cx_curve_t curve; + verificationAlgo *verificationFn; + cx_md_t hashId; + + switch (algorithmId) { + case ALGORITHM_ID_1: + curve = CX_CURVE_256K1; + verificationFn = (verificationAlgo*)cx_ecdsa_verify; + hashId = CX_SHA256; + break; + default: + PRINTF("Incorrect algorithmId %d\n", algorithmId); + THROW(0x6a80); + break; + } + offset += ALGORITHM_ID_SIZE; + PRINTF("hashing: %.*H\n", payloadSize, workBuffer); + cx_hash_sha256(workBuffer, payloadSize, hash, sizeof(hash)); + + if (dataLength < payloadSize + SIGNATURE_LENGTH_SIZE) { + PRINTF("Data too short to hold signature length\n"); + THROW(0x6a80); + } + + uint8_t signatureLen = workBuffer[offset]; + PRINTF("Sigature len: %d\n", signatureLen); + if (signatureLen < MIN_DER_SIG_SIZE || signatureLen > MAX_DER_SIG_SIZE) { + PRINTF("SignatureLen too big or too small. Must be between %d and %d, got %d\n", + MIN_DER_SIG_SIZE, + MAX_DER_SIG_SIZE, + signatureLen); + THROW(0x6a80); + } + offset += SIGNATURE_LENGTH_SIZE; + + if (dataLength < payloadSize + SIGNATURE_LENGTH_SIZE + signatureLen) { + PRINTF("Signature could not fit in data\n"); + THROW(0x6a80); + } + + cx_ecfp_init_public_key(curve, rawKey, rawKeyLen, &nftKey); + if (!verificationFn(&nftKey, + CX_LAST, + hashId, + hash, + sizeof(hash), + workBuffer + offset, + signatureLen)) { +#ifndef HAVE_BYPASS_SIGNATURES + PRINTF("Invalid NFT signature\n"); + THROW(0x6A80); +#endif + } + // Set this to `NOT_OLD_INTERNAL` because otherwise the tx might be treated as an + // internal plugin and we might get a collision and hence some BIG problems. + pluginType = NOT_OLD_INTERNAL; + + tmpCtx.transactionContext.tokenSet[tmpCtx.transactionContext.currentItemIndex] = 1; + THROW(0x9000); +} diff --git a/src_features/setExternalPlugin/cmd_setExternalPlugin.c b/src_features/setExternalPlugin/cmd_setExternalPlugin.c index 93c0b64..672bfaa 100644 --- a/src_features/setExternalPlugin/cmd_setExternalPlugin.c +++ b/src_features/setExternalPlugin/cmd_setExternalPlugin.c @@ -2,8 +2,8 @@ #include "apdu_constants.h" #include "ui_flow.h" #include "tokens.h" - -#define SELECTOR_SIZE 4 +#include "eth_plugin_interface.h" +#include "eth_plugin_internal.h" void handleSetExternalPlugin(uint8_t p1, uint8_t p2, @@ -14,17 +14,22 @@ void handleSetExternalPlugin(uint8_t p1, UNUSED(p1); UNUSED(p2); UNUSED(flags); - PRINTF("Handling set External Plugin\n"); - uint8_t hash[32]; + PRINTF("Handling set Plugin\n"); + uint8_t hash[INT256_LENGTH]; cx_ecfp_public_key_t tokenKey; uint8_t pluginNameLength = *workBuffer; + PRINTF("plugin Name Length: %d\n", pluginNameLength); const size_t payload_size = 1 + pluginNameLength + ADDRESS_LENGTH + SELECTOR_SIZE; if (dataLength <= payload_size) { + PRINTF("data too small: expected at least %d got %d\n", payload_size, dataLength); THROW(0x6A80); } if (pluginNameLength + 1 > sizeof(dataContext.tokenContext.pluginName)) { + PRINTF("name length too big: expected max %d, got %d\n", + sizeof(dataContext.tokenContext.pluginName), + pluginNameLength + 1); THROW(0x6A80); } @@ -41,8 +46,8 @@ void handleSetExternalPlugin(uint8_t p1, sizeof(hash), workBuffer + payload_size, dataLength - payload_size)) { - PRINTF("Invalid external plugin signature %.*H\n", payload_size, workBuffer); #ifndef HAVE_BYPASS_SIGNATURES + PRINTF("Invalid plugin signature %.*H\n", payload_size, workBuffer); THROW(0x6A80); #endif } @@ -77,10 +82,11 @@ void handleSetExternalPlugin(uint8_t p1, PRINTF("Plugin found\n"); - memmove(dataContext.tokenContext.contract_address, workBuffer, ADDRESS_LENGTH); + memmove(dataContext.tokenContext.contractAddress, workBuffer, ADDRESS_LENGTH); workBuffer += ADDRESS_LENGTH; - memmove(dataContext.tokenContext.method_selector, workBuffer, SELECTOR_SIZE); - externalPluginIsSet = true; + memmove(dataContext.tokenContext.methodSelector, workBuffer, SELECTOR_SIZE); + + pluginType = EXTERNAL; G_io_apdu_buffer[(*tx)++] = 0x90; G_io_apdu_buffer[(*tx)++] = 0x00; diff --git a/src_features/setPlugin/cmd_setPlugin.c b/src_features/setPlugin/cmd_setPlugin.c new file mode 100644 index 0000000..a81e9c9 --- /dev/null +++ b/src_features/setPlugin/cmd_setPlugin.c @@ -0,0 +1,294 @@ +#include "shared_context.h" +#include "apdu_constants.h" +#include "ui_flow.h" +#include "tokens.h" +#include "eth_plugin_interface.h" +#include "eth_plugin_internal.h" + +// Supported internal plugins +#define ERC721_STR "ERC721" +#define ERC1155_STR "ERC1155" + +#define TYPE_SIZE 1 +#define VERSION_SIZE 1 +#define PLUGIN_NAME_LENGTH_SIZE 1 +#define CHAIN_ID_SIZE 8 +#define KEY_ID_SIZE 1 +#define ALGORITHM_ID_SIZE 1 +#define SIGNATURE_LENGTH_SIZE 1 + +#define HEADER_SIZE TYPE_SIZE + VERSION_SIZE + PLUGIN_NAME_LENGTH_SIZE + +#define MIN_DER_SIG_SIZE 67 +#define MAX_DER_SIG_SIZE 72 + +typedef enum Type { + ETH_PLUGIN = 0x01, +} Type; + +typedef enum Version { + VERSION_1 = 0x01, +} Version; + +typedef enum KeyId { + TEST_KEY = 0x00, + PERSO_V2_KEY_1 = 0x01, + // Must ONLY be used with ERC721 and ERC1155 plugin + AWS_PLUGIN_KEY_1 = 0x02, +} KeyId; + +// Algorithm Id consists of a Key spec and an algorithm spec. +// Format is: KEYSPEC__ALGOSPEC +typedef enum AlgorithmID { + ECC_SECG_P256K1__ECDSA_SHA_256 = 0x01, +} AlgorithmID; + +#ifdef HAVE_NFT_TESTING_KEY +static const uint8_t LEDGER_TESTING_KEY[] = { + 0x04, 0xf5, 0x70, 0x0c, 0xa1, 0xe8, 0x74, 0x24, 0xc7, 0xc7, 0xd1, 0x19, 0xe7, + 0xe3, 0xc1, 0x89, 0xb1, 0x62, 0x50, 0x94, 0xdb, 0x6e, 0xa0, 0x40, 0x87, 0xc8, + 0x30, 0x00, 0x7d, 0x0b, 0x46, 0x9a, 0x53, 0x11, 0xee, 0x6a, 0x1a, 0xcd, 0x1d, + 0xa5, 0xaa, 0xb0, 0xf5, 0xc6, 0xdf, 0x13, 0x15, 0x8d, 0x28, 0xcc, 0x12, 0xd1, + 0xdd, 0xa6, 0xec, 0xe9, 0x46, 0xb8, 0x9d, 0x5c, 0x05, 0x49, 0x92, 0x59, 0xc4}; +#endif +static const uint8_t LEDGER_PERSO_V2_PUBLIC_KEY[] = {}; + +// Only used for signing NFT plugins (ERC721 and ERC1155) +static const uint8_t LEDGER_NFT_SELECTOR_PUBLIC_KEY[] = {}; + +// Verification function used to verify the signature +typedef bool verificationAlgo(const cx_ecfp_public_key_t *, + int, + cx_md_t, + const unsigned char *, + unsigned int, + unsigned char *, + unsigned int); + +// Returns the plugin type of a given plugin name. +// If the plugin name is not a specific known internal plugin, this function default return value is +// `EXERNAL`. +static pluginType_t getPluginType(char *pluginName, uint8_t pluginNameLength) { + if (pluginNameLength == sizeof(ERC721_STR) - 1 && + strncmp(pluginName, ERC721_STR, pluginNameLength) == 0) { + return ERC721; + } else if (pluginNameLength == sizeof(ERC1155_STR) - 1 && + strncmp(pluginName, ERC1155_STR, pluginNameLength) == 0) { + return ERC1155; + } else { + return EXTERNAL; + } +} + +void handleSetPlugin(uint8_t p1, + uint8_t p2, + uint8_t *workBuffer, + uint16_t dataLength, + unsigned int *flags, + unsigned int *tx) { + UNUSED(p1); + UNUSED(p2); + UNUSED(flags); + PRINTF("Handling set Plugin\n"); + uint8_t hash[INT256_LENGTH] = {0}; + cx_ecfp_public_key_t pluginKey = {0}; + tokenContext_t *tokenContext = &dataContext.tokenContext; + + uint8_t offset = 0; + + if (dataLength <= HEADER_SIZE) { + PRINTF("Data too small for headers: expected at least %d, got %d\n", + HEADER_SIZE, + dataLength); + THROW(0x6A80); + } + + enum Type type = workBuffer[offset]; + PRINTF("Type: %d\n", type); + switch (type) { + case ETH_PLUGIN: + break; + default: + PRINTF("Unsupported type %d\n", type); + THROW(0x6a80); + break; + } + offset += TYPE_SIZE; + + uint8_t version = workBuffer[offset]; + PRINTF("version: %d\n", version); + switch (version) { + case VERSION_1: + break; + default: + PRINTF("Unsupported version %d\n", version); + THROW(0x6a80); + break; + } + offset += VERSION_SIZE; + + uint8_t pluginNameLength = workBuffer[offset]; + offset += PLUGIN_NAME_LENGTH_SIZE; + + // Size of the payload (everything except the signature) + uint8_t payloadSize = HEADER_SIZE + pluginNameLength + ADDRESS_LENGTH + SELECTOR_SIZE + + CHAIN_ID_SIZE + KEY_ID_SIZE + ALGORITHM_ID_SIZE; + if (dataLength < payloadSize) { + PRINTF("Data too small for payload: expected at least %d, got %d\n", + payloadSize, + dataLength); + THROW(0x6A80); + } + + // `+ 1` because we want to add a null terminating character. + if (pluginNameLength + 1 > sizeof(tokenContext->pluginName)) { + PRINTF("plugin name too big: expected max %d, got %d\n", + sizeof(dataContext.tokenContext.pluginName), + pluginNameLength + 1); + THROW(0x6A80); + } + + // Safe because we've checked the size before. + memcpy(tokenContext->pluginName, workBuffer + offset, pluginNameLength); + tokenContext->pluginName[pluginNameLength] = '\0'; + + PRINTF("Length: %d\n", pluginNameLength); + PRINTF("plugin name: %s\n", tokenContext->pluginName); + offset += pluginNameLength; + + memcpy(tokenContext->contractAddress, workBuffer + offset, ADDRESS_LENGTH); + PRINTF("Address: %.*H\n", ADDRESS_LENGTH, workBuffer + offset); + offset += ADDRESS_LENGTH; + + memcpy(tokenContext->methodSelector, workBuffer + offset, SELECTOR_SIZE); + PRINTF("Selector: %.*H\n", SELECTOR_SIZE, tokenContext->methodSelector); + offset += SELECTOR_SIZE; + + // TODO: store chainID and assert that tx is using the same chainid. + // uint64_t chainid = u64_from_BE(workBuffer + offset, CHAIN_ID_SIZE); + // PRINTF("ChainID: %.*H\n", sizeof(chainid), &chainid); + offset += CHAIN_ID_SIZE; + + enum KeyId keyId = workBuffer[offset]; + uint8_t const *rawKey; + uint8_t rawKeyLen; + + PRINTF("KeyID: %d\n", keyId); + switch (keyId) { +#ifdef HAVE_NFT_TESTING_KEY + case TEST_KEY: + rawKey = LEDGER_TESTING_KEY; + rawKeyLen = sizeof(LEDGER_TESTING_KEY); + break; +#endif + case PERSO_V2_KEY_1: + rawKey = LEDGER_PERSO_V2_PUBLIC_KEY; + rawKeyLen = sizeof(LEDGER_PERSO_V2_PUBLIC_KEY); + break; + case AWS_PLUGIN_KEY_1: + rawKey = LEDGER_NFT_SELECTOR_PUBLIC_KEY; + rawKeyLen = sizeof(LEDGER_NFT_SELECTOR_PUBLIC_KEY); + break; + default: + PRINTF("KeyID %d not supported\n", keyId); + THROW(0x6A80); + break; + } + + PRINTF("RawKey: %.*H\n", rawKeyLen, rawKey); + offset += KEY_ID_SIZE; + + uint8_t algorithmId = workBuffer[offset]; + PRINTF("Algorithm: %d\n", algorithmId); + cx_curve_t curve; + verificationAlgo *verificationFn; + cx_md_t hashId; + + switch (algorithmId) { + case ECC_SECG_P256K1__ECDSA_SHA_256: + curve = CX_CURVE_256K1; + verificationFn = (verificationAlgo*)cx_ecdsa_verify; + hashId = CX_SHA256; + break; + default: + PRINTF("Incorrect algorithmId %d\n", algorithmId); + THROW(0x6a80); + break; + } + offset += ALGORITHM_ID_SIZE; + PRINTF("hashing: %.*H\n", payloadSize, workBuffer); + cx_hash_sha256(workBuffer, payloadSize, hash, sizeof(hash)); + + if (dataLength < payloadSize + SIGNATURE_LENGTH_SIZE) { + PRINTF("Data too short to hold signature length\n"); + THROW(0x6a80); + } + + uint8_t signatureLen = workBuffer[offset]; + PRINTF("Sigature len: %d\n", signatureLen); + if (signatureLen < MIN_DER_SIG_SIZE || signatureLen > MAX_DER_SIG_SIZE) { + PRINTF("SignatureLen too big or too small. Must be between %d and %d, got %d\n", + MIN_DER_SIG_SIZE, + MAX_DER_SIG_SIZE, + signatureLen); + THROW(0x6a80); + } + offset += SIGNATURE_LENGTH_SIZE; + + if (dataLength < payloadSize + SIGNATURE_LENGTH_SIZE + signatureLen) { + PRINTF("Signature could not fit in data\n"); + THROW(0x6a80); + } + + cx_ecfp_init_public_key(curve, rawKey, rawKeyLen, &pluginKey); + if (!verificationFn(&pluginKey, + CX_LAST, + hashId, + hash, + sizeof(hash), + workBuffer + offset, + signatureLen)) { +#ifndef HAVE_BYPASS_SIGNATURES + PRINTF("Invalid NFT signature\n"); + THROW(0x6A80); +#endif + } + + pluginType = getPluginType(tokenContext->pluginName, pluginNameLength); + if (keyId == AWS_PLUGIN_KEY_1) { + if (pluginType != ERC721 && pluginType != ERC1155) { + PRINTF("AWS key must only be used to set NFT internal plugins\n"); + THROW(0x6A80); + } + } + + switch (pluginType) { + case EXTERNAL: { + PRINTF("Check external plugin %s\n", tokenContext->pluginName); + + // Check if the plugin is present on the device + uint32_t params[2]; + params[0] = (uint32_t) tokenContext->pluginName; + params[1] = ETH_PLUGIN_CHECK_PRESENCE; + BEGIN_TRY { + TRY { + os_lib_call(params); + } + CATCH_OTHER(e) { + PRINTF("%s external plugin is not present\n", tokenContext->pluginName); + memset(tokenContext->pluginName, 0, sizeof(tokenContext->pluginName)); + THROW(0x6984); + } + FINALLY { + } + } + END_TRY; + break; + } + default: + break; + } + + G_io_apdu_buffer[(*tx)++] = 0x90; + G_io_apdu_buffer[(*tx)++] = 0x00; +} diff --git a/src_features/signTx/cmd_signTx.c b/src_features/signTx/cmd_signTx.c index 09c0f8c..072abc5 100644 --- a/src_features/signTx/cmd_signTx.c +++ b/src_features/signTx/cmd_signTx.c @@ -2,6 +2,7 @@ #include "apdu_constants.h" #include "ui_flow.h" #include "feature_signTx.h" +#include "eth_plugin_interface.h" void handleSign(uint8_t p1, uint8_t p2, @@ -53,7 +54,7 @@ void handleSign(uint8_t p1, workBuffer++; dataLength--; } else { - PRINTF("Transaction type not supported\n"); + PRINTF("Transaction type %d not supported\n", txType); THROW(0x6501); } } else { diff --git a/src_features/signTx/logic_signTx.c b/src_features/signTx/logic_signTx.c index 8b9339b..6c65cff 100644 --- a/src_features/signTx/logic_signTx.c +++ b/src_features/signTx/logic_signTx.c @@ -65,6 +65,7 @@ customStatus_e customProcessor(txContext_t *context) { PRINTF("pluginstatus %d\n", dataContext.tokenContext.pluginStatus); eth_plugin_result_t status = dataContext.tokenContext.pluginStatus; if (status == ETH_PLUGIN_RESULT_ERROR) { + PRINTF("Plugin error\n"); return CUSTOM_FAULT; } else if (status >= ETH_PLUGIN_RESULT_SUCCESSFUL) { dataContext.tokenContext.fieldIndex = 0; @@ -113,6 +114,7 @@ customStatus_e customProcessor(txContext_t *context) { copySize); if (context->currentFieldPos == context->currentFieldLength) { + PRINTF("\n\nIncrementing one\n"); context->currentField++; context->processingField = false; } @@ -238,7 +240,6 @@ static void feesToString(uint256_t *rawFee, char *displayBuffer, uint32_t displa i++; } displayBuffer[tickerOffset + i] = '\0'; - PRINTF("Displayed fees: %s\n", displayBuffer); } // Compute the fees, transform it to a string, prepend a ticker to it and copy everything to @@ -301,7 +302,6 @@ void finalizeParsing(bool direct) { // Verify the chain if (chainConfig->chainId != ETHEREUM_MAINNET_CHAINID) { - // TODO: Could we remove above check? uint64_t id = get_chain_id(); if (chainConfig->chainId != id) { @@ -338,24 +338,24 @@ void finalizeParsing(bool direct) { } } // Lookup tokens if requested - ethPluginProvideToken_t pluginProvideToken; - eth_plugin_prepare_provide_token(&pluginProvideToken); + ethPluginProvideInfo_t pluginProvideInfo; + eth_plugin_prepare_provide_info(&pluginProvideInfo); if ((pluginFinalize.tokenLookup1 != NULL) || (pluginFinalize.tokenLookup2 != NULL)) { if (pluginFinalize.tokenLookup1 != NULL) { PRINTF("Lookup1: %.*H\n", ADDRESS_LENGTH, pluginFinalize.tokenLookup1); - pluginProvideToken.token1 = getKnownToken(pluginFinalize.tokenLookup1); - if (pluginProvideToken.token1 != NULL) { - PRINTF("Token1 ticker: %s\n", pluginProvideToken.token1->ticker); + pluginProvideInfo.item1 = getKnownToken(pluginFinalize.tokenLookup1); + if (pluginProvideInfo.item1 != NULL) { + PRINTF("Token1 ticker: %s\n", pluginProvideInfo.item1->token.ticker); } } if (pluginFinalize.tokenLookup2 != NULL) { PRINTF("Lookup2: %.*H\n", ADDRESS_LENGTH, pluginFinalize.tokenLookup2); - pluginProvideToken.token2 = getKnownToken(pluginFinalize.tokenLookup2); - if (pluginProvideToken.token2 != NULL) { - PRINTF("Token2 ticker: %s\n", pluginProvideToken.token2->ticker); + pluginProvideInfo.item2 = getKnownToken(pluginFinalize.tokenLookup2); + if (pluginProvideInfo.item2 != NULL) { + PRINTF("Token2 ticker: %s\n", pluginProvideInfo.item2->token.ticker); } } - if (eth_plugin_call(ETH_PLUGIN_PROVIDE_TOKEN, (void *) &pluginProvideToken) <= + if (eth_plugin_call(ETH_PLUGIN_PROVIDE_INFO, (void *) &pluginProvideInfo) <= ETH_PLUGIN_RESULT_UNSUCCESSFUL) { PRINTF("Plugin provide token call failed\n"); reportFinalizeError(direct); @@ -363,7 +363,7 @@ void finalizeParsing(bool direct) { return; } } - pluginFinalize.result = pluginProvideToken.result; + pluginFinalize.result = pluginProvideInfo.result; } if (pluginFinalize.result != ETH_PLUGIN_RESULT_FALLBACK) { // Handle the right interface @@ -373,7 +373,7 @@ void finalizeParsing(bool direct) { // Add the number of screens + the number of additional screens to get the total // number of screens needed. dataContext.tokenContext.pluginUiMaxItems = - pluginFinalize.numScreens + pluginProvideToken.additionalScreens; + pluginFinalize.numScreens + pluginProvideInfo.additionalScreens; break; case ETH_UI_TYPE_AMOUNT_ADDRESS: genericUI = true; @@ -389,9 +389,9 @@ void finalizeParsing(bool direct) { tmpContent.txContent.value.length = 32; memmove(tmpContent.txContent.destination, pluginFinalize.address, 20); tmpContent.txContent.destinationLength = 20; - if (pluginProvideToken.token1 != NULL) { - decimals = pluginProvideToken.token1->decimals; - ticker = pluginProvideToken.token1->ticker; + if (pluginProvideInfo.item1 != NULL) { + decimals = pluginProvideInfo.item1->token.decimals; + ticker = pluginProvideInfo.item1->token.ticker; } break; default: diff --git a/src_features/stark_provideQuantum/cmd_stark_provideQuantum.c b/src_features/stark_provideQuantum/cmd_stark_provideQuantum.c index 15f6d16..4d390dd 100644 --- a/src_features/stark_provideQuantum/cmd_stark_provideQuantum.c +++ b/src_features/stark_provideQuantum/cmd_stark_provideQuantum.c @@ -37,19 +37,19 @@ void handleStarkwareProvideQuantum(uint8_t p1, addressZero = allzeroes(dataBuffer, 20); } if ((p1 != STARK_QUANTUM_ETH) && !addressZero) { - for (i = 0; i < MAX_TOKEN; i++) { - currentToken = &tmpCtx.transactionContext.tokens[i]; + for (i = 0; i < MAX_ITEMS; i++) { + currentToken = &tmpCtx.transactionContext.extraInfo[i].token; if (tmpCtx.transactionContext.tokenSet[i] && (memcmp(currentToken->address, dataBuffer, 20) == 0)) { break; } } - if (i == MAX_TOKEN) { + if (i == MAX_ITEMS) { PRINTF("Associated token not found\n"); THROW(0x6A80); } } else { - i = MAX_TOKEN; + i = MAX_ITEMS; } memmove(dataContext.tokenContext.quantum, dataBuffer + 20, 32); if (p1 != STARK_QUANTUM_LEGACY) { diff --git a/src_plugins/compound/compound_plugin.c b/src_plugins/compound/compound_plugin.c index 66437a8..a933d89 100644 --- a/src_plugins/compound/compound_plugin.c +++ b/src_plugins/compound/compound_plugin.c @@ -85,6 +85,7 @@ void compound_plugin_call(int message, void *parameters) { // 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; } @@ -148,12 +149,12 @@ void compound_plugin_call(int message, void *parameters) { msg->result = ETH_PLUGIN_RESULT_OK; } break; - case ETH_PLUGIN_PROVIDE_TOKEN: { - ethPluginProvideToken_t *msg = (ethPluginProvideToken_t *) parameters; + 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->token1 != NULL)); - if (msg->token1 != NULL) { - strlcpy(context->ticker_1, msg->token1->ticker, MAX_TICKER_LEN); + 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: @@ -166,7 +167,7 @@ void compound_plugin_call(int message, void *parameters) { // Only case where we use the compound contract decimals case COMPOUND_REDEEM: - context->decimals = msg->token1->decimals; + context->decimals = msg->item1->token.decimals; msg->result = ETH_PLUGIN_RESULT_OK; break; diff --git a/src_plugins/erc1155/erc1155_plugin.c b/src_plugins/erc1155/erc1155_plugin.c new file mode 100644 index 0000000..20b6953 --- /dev/null +++ b/src_plugins/erc1155/erc1155_plugin.c @@ -0,0 +1,136 @@ +#include "erc1155_plugin.h" +#include "eth_plugin_internal.h" + +static const uint8_t ERC1155_APPROVE_FOR_ALL_SELECTOR[SELECTOR_SIZE] = {0xa2, 0x2c, 0xb4, 0x65}; +static const uint8_t ERC1155_SAFE_TRANSFER_SELECTOR[SELECTOR_SIZE] = {0xf2, 0x42, 0x43, 0x2a}; +static const uint8_t ERC1155_SAFE_BATCH_TRANSFER[SELECTOR_SIZE] = {0xf2, 0x42, 0x43, 0x2a}; + +const uint8_t *const ERC1155_SELECTORS[NUM_ERC1155_SELECTORS] = { + ERC1155_APPROVE_FOR_ALL_SELECTOR, + ERC1155_SAFE_TRANSFER_SELECTOR, + ERC1155_SAFE_BATCH_TRANSFER, +}; + +static void handle_init_contract(void *parameters) { + ethPluginInitContract_t *msg = (ethPluginInitContract_t *) parameters; + erc1155_context_t *context = (erc1155_context_t *) msg->pluginContext; + + uint8_t i; + for (i = 0; i < NUM_ERC1155_SELECTORS; i++) { + if (memcmp((uint8_t *) PIC(ERC1155_SELECTORS[i]), msg->selector, SELECTOR_SIZE) == 0) { + context->selectorIndex = i; + break; + } + } + + // No selector found. + if (i == NUM_ERC1155_SELECTORS) { + PRINTF("Unknown erc1155 selector %.*H\n", SELECTOR_SIZE, msg->selector); + msg->result = ETH_PLUGIN_RESULT_FALLBACK; + return; + } + + msg->result = ETH_PLUGIN_RESULT_OK; + switch (context->selectorIndex) { + case SAFE_TRANSFER: + case SAFE_BATCH_TRANSFER: + context->next_param = FROM; + break; + case SET_APPROVAL_FOR_ALL: + context->next_param = OPERATOR; + break; + default: + PRINTF("Unsupported selector index: %d\n", context->selectorIndex); + msg->result = ETH_PLUGIN_RESULT_ERROR; + break; + } +} + +static void handle_finalize(void *parameters) { + ethPluginFinalize_t *msg = (ethPluginFinalize_t *) parameters; + erc1155_context_t *context = (erc1155_context_t *) msg->pluginContext; + + if (context->selectorIndex != SAFE_BATCH_TRANSFER) { + msg->tokenLookup1 = msg->pluginSharedRO->txContent->destination; + } else { + msg->tokenLookup1 = NULL; + } + + msg->tokenLookup2 = NULL; + switch (context->selectorIndex) { + case SAFE_TRANSFER: + msg->numScreens = 4; + break; + case SET_APPROVAL_FOR_ALL: + case SAFE_BATCH_TRANSFER: + msg->numScreens = 3; + break; + default: + msg->result = ETH_PLUGIN_RESULT_ERROR; + return; + } + // Check if some ETH is attached to this tx + if (!allzeroes((void *) &msg->pluginSharedRO->txContent->value, + sizeof(msg->pluginSharedRO->txContent->value))) { + // Those functions are not payable so return an error. + msg->result = ETH_PLUGIN_RESULT_ERROR; + return; + } + msg->uiType = ETH_UI_TYPE_GENERIC; + msg->result = ETH_PLUGIN_RESULT_OK; +} + +static void handle_provide_info(void *parameters) { + ethPluginProvideInfo_t *msg = (ethPluginProvideInfo_t *) parameters; + + msg->result = ETH_PLUGIN_RESULT_OK; +} + +static void handle_query_contract_id(void *parameters) { + ethQueryContractID_t *msg = (ethQueryContractID_t *) parameters; + erc1155_context_t *context = (erc1155_context_t *) msg->pluginContext; + + msg->result = ETH_PLUGIN_RESULT_OK; + + strlcpy(msg->name, "NFT", msg->nameLength); + + switch (context->selectorIndex) { + case SET_APPROVAL_FOR_ALL: + strlcpy(msg->version, "Allowance", msg->versionLength); + break; + case SAFE_TRANSFER: + case SAFE_BATCH_TRANSFER: + strlcpy(msg->version, "Transfer", msg->versionLength); + break; + default: + PRINTF("Unsupported selector %d\n", context->selectorIndex); + msg->result = ETH_PLUGIN_RESULT_ERROR; + break; + } +} + +void erc1155_plugin_call(int message, void *parameters) { + switch (message) { + case ETH_PLUGIN_INIT_CONTRACT: { + handle_init_contract(parameters); + } break; + case ETH_PLUGIN_PROVIDE_PARAMETER: { + handle_provide_parameter_1155(parameters); + } break; + case ETH_PLUGIN_FINALIZE: { + handle_finalize(parameters); + } break; + case ETH_PLUGIN_PROVIDE_INFO: { + handle_provide_info(parameters); + } break; + case ETH_PLUGIN_QUERY_CONTRACT_ID: { + handle_query_contract_id(parameters); + } break; + case ETH_PLUGIN_QUERY_CONTRACT_UI: { + handle_query_contract_ui_1155(parameters); + } break; + default: + PRINTF("Unhandled message %d\n", message); + break; + } +} diff --git a/src_plugins/erc1155/erc1155_plugin.h b/src_plugins/erc1155/erc1155_plugin.h new file mode 100644 index 0000000..fefd165 --- /dev/null +++ b/src_plugins/erc1155/erc1155_plugin.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include "eth_plugin_handler.h" +#include "shared_context.h" +#include "ethUtils.h" +#include "utils.h" + +// Internal plugin for EIP 1155: https://eips.ethereum.org/EIPS/eip-1155 + +#define NUM_ERC1155_SELECTORS 3 + +typedef enum { + SET_APPROVAL_FOR_ALL, + SAFE_TRANSFER, + SAFE_BATCH_TRANSFER, +} erc1155_selector_t; + +typedef enum { + FROM, + TO, + TOKEN_IDS_OFFSET, + TOKEN_IDS_LENGTH, + TOKEN_ID, + VALUE_OFFSET, + VALUE_LENGTH, + VALUE, + OPERATOR, + APPROVED, + NONE, +} erc1155_selector_field; + +typedef struct erc1155_context_t { + uint8_t address[ADDRESS_LENGTH]; + uint8_t tokenId[INT256_LENGTH]; + uint8_t value[INT256_LENGTH]; + + uint32_t valueOffset; + uint32_t tokenIdsOffset; + uint32_t targetOffset; + + bool approved; + erc1155_selector_field next_param; + uint8_t selectorIndex; +} erc1155_context_t; + +// TODO: Find out why there is a duplicate if we remove 1155 suffix +void handle_provide_parameter_1155(void *parameters); +void handle_query_contract_ui_1155(void *parameters); \ No newline at end of file diff --git a/src_plugins/erc1155/erc1155_provide_parameters.c b/src_plugins/erc1155/erc1155_provide_parameters.c new file mode 100644 index 0000000..8bbfb79 --- /dev/null +++ b/src_plugins/erc1155/erc1155_provide_parameters.c @@ -0,0 +1,106 @@ +#include "erc1155_plugin.h" +#include "eth_plugin_internal.h" + +static void handle_safe_transfer(ethPluginProvideParameter_t *msg, erc1155_context_t *context) { + switch (context->next_param) { + case FROM: + context->next_param = TO; + break; + case TO: + copy_address(context->address, msg->parameter, sizeof(context->address)); + context->next_param = TOKEN_ID; + break; + case TOKEN_ID: + copy_parameter(context->tokenId, msg->parameter, sizeof(context->tokenId)); + context->next_param = VALUE; + break; + case VALUE: + copy_parameter(context->value, msg->parameter, sizeof(context->value)); + context->next_param = NONE; + break; + default: + // Some extra data might be present so don't error. + break; + } +} + +static void handle_batch_transfer(ethPluginProvideParameter_t *msg, erc1155_context_t *context) { + switch (context->next_param) { + case FROM: + context->next_param = TO; + break; + case TO: + copy_address(context->address, msg->parameter, sizeof(context->address)); + context->next_param = TOKEN_ID; + break; + case TOKEN_IDS_OFFSET: + context->tokenIdsOffset = U4BE(msg->parameter, PARAMETER_LENGTH - 4); + context->next_param = VALUE_OFFSET; + break; + case VALUE_OFFSET: + context->targetOffset = context->tokenIdsOffset; + context->next_param = TOKEN_ID; + break; + case TOKEN_ID: + copy_parameter(context->tokenId, msg->parameter, sizeof(context->tokenId)); + context->targetOffset = context->valueOffset; + context->next_param = VALUE; + break; + case VALUE: + copy_parameter(context->value, msg->parameter, sizeof(context->value)); + context->targetOffset = 0; + context->next_param = NONE; + default: + // Some extra data might be present so don't error. + break; + } +} + +static void handle_approval_for_all(ethPluginProvideParameter_t *msg, erc1155_context_t *context) { + switch (context->next_param) { + case OPERATOR: + context->next_param = APPROVED; + copy_address(context->address, msg->parameter, sizeof(context->address)); + break; + case APPROVED: + context->approved = msg->parameter[PARAMETER_LENGTH - 1]; + context->next_param = NONE; + break; + default: + PRINTF("Param %d not supported\n", context->next_param); + msg->result = ETH_PLUGIN_RESULT_ERROR; + break; + } +} + +void handle_provide_parameter_1155(void *parameters) { + ethPluginProvideParameter_t *msg = (ethPluginProvideParameter_t *) parameters; + erc1155_context_t *context = (erc1155_context_t *) msg->pluginContext; + + PRINTF("erc1155 plugin provide parameter %d %.*H\n", + msg->parameterOffset, + PARAMETER_LENGTH, + msg->parameter); + + msg->result = ETH_PLUGIN_RESULT_SUCCESSFUL; + + if (context->targetOffset > SELECTOR_SIZE && + context->targetOffset != msg->parameterOffset - SELECTOR_SIZE) { + return; + } + switch (context->selectorIndex) { + case SAFE_TRANSFER: + handle_safe_transfer(msg, context); + break; + case SAFE_BATCH_TRANSFER: + handle_batch_transfer(msg, context); + break; + case SET_APPROVAL_FOR_ALL: + handle_approval_for_all(msg, context); + break; + default: + PRINTF("Selector index %d not supported\n", context->selectorIndex); + msg->result = ETH_PLUGIN_RESULT_ERROR; + break; + } +} \ No newline at end of file diff --git a/src_plugins/erc1155/erc1155_ui.c b/src_plugins/erc1155/erc1155_ui.c new file mode 100644 index 0000000..2885233 --- /dev/null +++ b/src_plugins/erc1155/erc1155_ui.c @@ -0,0 +1,135 @@ +#include "erc1155_plugin.h" + +static void set_approval_for_all_ui(ethQueryContractUI_t *msg, erc1155_context_t *context) { + switch (msg->screenIndex) { + case 0: + if (context->approved) { + strlcpy(msg->title, "Allow", msg->titleLength); + } else { + strlcpy(msg->title, "Revoke", msg->titleLength); + } + getEthDisplayableAddress(context->address, + msg->msg, + msg->msgLength, + &global_sha3, + chainConfig->chainId); + break; + case 1: + strlcpy(msg->title, "To Manage ALL", msg->titleLength); + if (msg->item1) { + strlcpy(msg->msg, (const char *) &msg->item1->nft.collectionName, msg->msgLength); + } else { + strlcpy(msg->msg, "Not found", msg->msgLength); + } + break; + case 2: + strlcpy(msg->title, "NFT Address", msg->titleLength); + getEthDisplayableAddress(msg->pluginSharedRO->txContent->destination, + msg->msg, + msg->msgLength, + &global_sha3, + chainConfig->chainId); + break; + default: + PRINTF("Unsupported screen index %d\n", msg->screenIndex); + msg->result = ETH_PLUGIN_RESULT_ERROR; + break; + } +} + +static void set_transfer_ui(ethQueryContractUI_t *msg, erc1155_context_t *context) { + switch (msg->screenIndex) { + case 0: + // What will be displayed on the screen is: + // | Send | + // | `X` `COLLECTION_NAME` | + // where `X` is `value` + strlcpy(msg->title, "Send", msg->titleLength); + uint256_to_decimal(context->value, sizeof(context->value), msg->msg, msg->msgLength); + strlcat(msg->msg, " ", msg->msgLength); + if (msg->item1) { + strlcat(msg->msg, (const char *) &msg->item1->nft.collectionName, msg->msgLength); + } else { + strlcat(msg->msg, "Items", msg->msgLength); + } + break; + case 1: + strlcpy(msg->title, "To", msg->titleLength); + getEthDisplayableAddress(context->address, + msg->msg, + msg->msgLength, + &global_sha3, + chainConfig->chainId); + break; + case 2: + strlcpy(msg->title, "NFT Address", msg->titleLength); + getEthDisplayableAddress(msg->pluginSharedRO->txContent->destination, + msg->msg, + msg->msgLength, + &global_sha3, + chainConfig->chainId); + break; + case 3: + strlcpy(msg->title, "NFT ID", msg->titleLength); + uint256_to_decimal(context->tokenId, + sizeof(context->tokenId), + msg->msg, + msg->msgLength); + break; + default: + PRINTF("Unsupported screen index %d\n", msg->screenIndex); + msg->result = ETH_PLUGIN_RESULT_ERROR; + break; + } +} + +static void set_batch_transfer_ui(ethQueryContractUI_t *msg, erc1155_context_t *context) { + switch (msg->screenIndex) { + case 0: + strlcpy(msg->title, "Send NFTs From", msg->titleLength); + uint256_to_decimal(context->value, sizeof(context->value), msg->msg, msg->msgLength); + strlcat(msg->msg, " Different Collections", msg->msgLength); + break; + case 1: + strlcpy(msg->title, "To", msg->titleLength); + getEthDisplayableAddress(context->address, + msg->msg, + msg->msgLength, + &global_sha3, + chainConfig->chainId); + case 2: + strlcpy(msg->title, "NFT Address", msg->titleLength); + getEthDisplayableAddress(msg->pluginSharedRO->txContent->destination, + msg->msg, + msg->msgLength, + &global_sha3, + chainConfig->chainId); + break; + default: + PRINTF("Unsupported screen index %d\n", msg->screenIndex); + msg->result = ETH_PLUGIN_RESULT_ERROR; + break; + } +} + +void handle_query_contract_ui_1155(void *parameters) { + ethQueryContractUI_t *msg = (ethQueryContractUI_t *) parameters; + erc1155_context_t *context = (erc1155_context_t *) msg->pluginContext; + + msg->result = ETH_PLUGIN_RESULT_OK; + switch (context->selectorIndex) { + case SET_APPROVAL_FOR_ALL: + set_approval_for_all_ui(msg, context); + break; + case SAFE_TRANSFER: + set_transfer_ui(msg, context); + break; + case SAFE_BATCH_TRANSFER: + set_batch_transfer_ui(msg, context); + break; + default: + msg->result = ETH_PLUGIN_RESULT_ERROR; + PRINTF("Unsupported selector index %d\n", context->selectorIndex); + break; + } +} \ No newline at end of file diff --git a/src_plugins/erc20/erc20_plugin.c b/src_plugins/erc20/erc20_plugin.c index 7903402..04062d8 100644 --- a/src_plugins/erc20/erc20_plugin.c +++ b/src_plugins/erc20/erc20_plugin.c @@ -159,16 +159,16 @@ void erc20_plugin_call(int message, void *parameters) { } } break; - case ETH_PLUGIN_PROVIDE_TOKEN: { - ethPluginProvideToken_t *msg = (ethPluginProvideToken_t *) parameters; + case ETH_PLUGIN_PROVIDE_INFO: { + ethPluginProvideInfo_t *msg = (ethPluginProvideInfo_t *) parameters; erc20_parameters_t *context = (erc20_parameters_t *) msg->pluginContext; PRINTF("erc20 plugin provide token 1: %d - 2: %d\n", - (msg->token1 != NULL), - (msg->token2 != NULL)); - if (msg->token1 != NULL) { + (msg->item1 != NULL), + (msg->item2 != NULL)); + if (msg->item1 != NULL) { context->target = TARGET_ADDRESS; - strlcpy(context->ticker, msg->token1->ticker, MAX_TICKER_LEN); - context->decimals = msg->token1->decimals; + strlcpy(context->ticker, msg->item1->token.ticker, MAX_TICKER_LEN); + context->decimals = msg->item1->token.decimals; if (context->selectorIndex == ERC20_APPROVE) { if (check_contract(context)) { context->target = TARGET_CONTRACT; diff --git a/src_plugins/erc721/erc721_plugin.c b/src_plugins/erc721/erc721_plugin.c index 9d04d74..c9542d8 100644 --- a/src_plugins/erc721/erc721_plugin.c +++ b/src_plugins/erc721/erc721_plugin.c @@ -1,151 +1,146 @@ -#include +#include "erc721_plugin.h" #include "eth_plugin_internal.h" -#include "eth_plugin_handler.h" -#include "shared_context.h" -#include "ethUtils.h" -#include "utils.h" -typedef struct erc721_parameters_t { - uint8_t selectorIndex; - uint8_t address[ADDRESS_LENGTH]; - uint8_t tokenId[INT256_LENGTH]; - // tokenDefinition_t *tokenSelf; - // tokenDefinition_t *tokenAddress; -} erc721_parameters_t; +static const uint8_t ERC721_APPROVE_SELECTOR[SELECTOR_SIZE] = {0x13, 0x37, 0x42, 0x42}; +static const uint8_t ERC721_APPROVE_FOR_ALL_SELECTOR[SELECTOR_SIZE] = {0xa2, 0x2c, 0xb4, 0x65}; +static const uint8_t ERC721_TRANSFER_SELECTOR[SELECTOR_SIZE] = {0x23, 0xb8, 0x72, 0xdd}; +static const uint8_t ERC721_SAFE_TRANSFER_SELECTOR[SELECTOR_SIZE] = {0x42, 0x84, 0x2e, 0x0e}; +static const uint8_t ERC721_SAFE_TRANSFER_DATA_SELECTOR[SELECTOR_SIZE] = {0xf2, 0x42, 0x43, 0x2a}; -bool erc721_plugin_available_check() { -#ifdef HAVE_STARKWARE - if (quantumSet) { - switch (dataContext.tokenContext.quantumType) { - case STARK_QUANTUM_ERC721: - case STARK_QUANTUM_MINTABLE_ERC721: - return true; - default: - return false; +const uint8_t *const ERC721_SELECTORS[NUM_ERC721_SELECTORS] = { + ERC721_APPROVE_SELECTOR, + ERC721_APPROVE_FOR_ALL_SELECTOR, + ERC721_TRANSFER_SELECTOR, + ERC721_SAFE_TRANSFER_SELECTOR, + ERC721_SAFE_TRANSFER_DATA_SELECTOR, +}; + +static void handle_init_contract(void *parameters) { + ethPluginInitContract_t *msg = (ethPluginInitContract_t *) parameters; + erc721_context_t *context = (erc721_context_t *) msg->pluginContext; + + uint8_t i; + for (i = 0; i < NUM_ERC721_SELECTORS; i++) { + if (memcmp((uint8_t *) PIC(ERC721_SELECTORS[i]), msg->selector, SELECTOR_SIZE) == 0) { + context->selectorIndex = i; + break; } } - return false; -#endif + + // No selector found. + if (i == NUM_ERC721_SELECTORS) { + PRINTF("Unknown erc721 selector %.*H\n", SELECTOR_SIZE, msg->selector); + msg->result = ETH_PLUGIN_RESULT_FALLBACK; + return; + } + + msg->result = ETH_PLUGIN_RESULT_OK; + switch (context->selectorIndex) { + case SET_APPROVAL_FOR_ALL: + case APPROVE: + context->next_param = OPERATOR; + break; + case SAFE_TRANSFER: + case SAFE_TRANSFER_DATA: + case TRANSFER: + context->next_param = FROM; + break; + default: + PRINTF("Unsupported selector index: %d\n", context->selectorIndex); + msg->result = ETH_PLUGIN_RESULT_ERROR; + break; + } +} + +static void handle_finalize(void *parameters) { + ethPluginFinalize_t *msg = (ethPluginFinalize_t *) parameters; + erc721_context_t *context = (erc721_context_t *) msg->pluginContext; + + msg->tokenLookup1 = msg->pluginSharedRO->txContent->destination; + msg->tokenLookup2 = NULL; + switch (context->selectorIndex) { + case TRANSFER: + case SAFE_TRANSFER: + case SAFE_TRANSFER_DATA: + case SET_APPROVAL_FOR_ALL: + msg->numScreens = 3; + break; + case APPROVE: + msg->numScreens = 4; + break; + default: + msg->result = ETH_PLUGIN_RESULT_ERROR; + return; + } + // Check if some ETH is attached to this tx + if (!allzeroes((void *) &msg->pluginSharedRO->txContent->value, + sizeof(msg->pluginSharedRO->txContent->value))) { + // Set Approval for All is not payable + if (context->selectorIndex == SET_APPROVAL_FOR_ALL) { + msg->result = ETH_PLUGIN_RESULT_ERROR; + return; + } else { + // Add an additional screen + msg->numScreens++; + } + } + msg->uiType = ETH_UI_TYPE_GENERIC; + msg->result = ETH_PLUGIN_RESULT_OK; +} + +static void handle_provide_info(void *parameters) { + ethPluginProvideInfo_t *msg = (ethPluginProvideInfo_t *) parameters; + + msg->result = ETH_PLUGIN_RESULT_OK; +} + +static void handle_query_contract_id(void *parameters) { + ethQueryContractID_t *msg = (ethQueryContractID_t *) parameters; + erc721_context_t *context = (erc721_context_t *) msg->pluginContext; + + msg->result = ETH_PLUGIN_RESULT_OK; + + strlcpy(msg->name, "NFT", msg->nameLength); + + switch (context->selectorIndex) { + case SET_APPROVAL_FOR_ALL: + case APPROVE: + strlcpy(msg->version, "Allowance", msg->versionLength); + break; + case SAFE_TRANSFER: + case SAFE_TRANSFER_DATA: + case TRANSFER: + strlcpy(msg->version, "Transfer", msg->versionLength); + break; + default: + PRINTF("Unsupported selector %d\n", context->selectorIndex); + msg->result = ETH_PLUGIN_RESULT_ERROR; + break; + } } void erc721_plugin_call(int message, void *parameters) { switch (message) { case ETH_PLUGIN_INIT_CONTRACT: { - ethPluginInitContract_t *msg = (ethPluginInitContract_t *) parameters; - erc721_parameters_t *context = (erc721_parameters_t *) msg->pluginContext; - // enforce that ETH amount should be 0 - if (!allzeroes(msg->pluginSharedRO->txContent->value.value, 32)) { - PRINTF("Err: Transaction amount is not 0 for erc721 approval\n"); - msg->result = ETH_PLUGIN_RESULT_ERROR; - } else { - size_t i; - for (i = 0; i < NUM_ERC721_SELECTORS; i++) { - if (memcmp((uint8_t *) PIC(ERC721_SELECTORS[i]), - msg->selector, - SELECTOR_SIZE) == 0) { - context->selectorIndex = i; - break; - } - } - if (i == NUM_ERC721_SELECTORS) { - PRINTF("Unknown erc721 selector %.*H\n", SELECTOR_SIZE, msg->selector); - break; - } - if (msg->dataSize != 4 + 32 + 32) { - PRINTF("Invalid erc721 approval data size %d\n", msg->dataSize); - break; - } - PRINTF("erc721 plugin init\n"); - msg->result = ETH_PLUGIN_RESULT_OK; - } + handle_init_contract(parameters); } break; - case ETH_PLUGIN_PROVIDE_PARAMETER: { - ethPluginProvideParameter_t *msg = (ethPluginProvideParameter_t *) parameters; - erc721_parameters_t *context = (erc721_parameters_t *) msg->pluginContext; - PRINTF("erc721 plugin provide parameter %d %.*H\n", - msg->parameterOffset, - 32, - msg->parameter); - switch (msg->parameterOffset) { - case 4: - memmove(context->address, msg->parameter + 32 - 20, 20); - msg->result = ETH_PLUGIN_RESULT_OK; - break; - case 4 + 32: - memmove(context->tokenId, msg->parameter, 32); - msg->result = ETH_PLUGIN_RESULT_OK; - break; - default: - PRINTF("Unhandled parameter offset\n"); - break; - } + handle_provide_parameter(parameters); } break; - case ETH_PLUGIN_FINALIZE: { - ethPluginFinalize_t *msg = (ethPluginFinalize_t *) parameters; - erc721_parameters_t *context = (erc721_parameters_t *) msg->pluginContext; - PRINTF("erc721 plugin finalize\n"); - msg->tokenLookup1 = msg->pluginSharedRO->txContent->destination; - msg->tokenLookup2 = context->address; - msg->numScreens = 3; - msg->uiType = ETH_UI_TYPE_GENERIC; - msg->result = ETH_PLUGIN_RESULT_OK; + handle_finalize(parameters); } break; - - case ETH_PLUGIN_PROVIDE_TOKEN: { - ethPluginProvideToken_t *msg = (ethPluginProvideToken_t *) parameters; - PRINTF("erc721 plugin provide token dest: %d - address: %d\n", - (msg->token1 != NULL), - (msg->token2 != NULL)); - // context->tokenSelf = msg->token1; - // context->tokenAddress = msg->token2; - msg->result = ETH_PLUGIN_RESULT_OK; + case ETH_PLUGIN_PROVIDE_INFO: { + handle_provide_info(parameters); } break; - case ETH_PLUGIN_QUERY_CONTRACT_ID: { - ethQueryContractID_t *msg = (ethQueryContractID_t *) parameters; - strlcpy(msg->name, "Allowance", msg->nameLength); - strlcpy(msg->version, "", msg->versionLength); - msg->result = ETH_PLUGIN_RESULT_OK; + handle_query_contract_id(parameters); } break; - case ETH_PLUGIN_QUERY_CONTRACT_UI: { - ethQueryContractUI_t *msg = (ethQueryContractUI_t *) parameters; - erc721_parameters_t *context = (erc721_parameters_t *) msg->pluginContext; - switch (msg->screenIndex) { - case 0: - strlcpy(msg->title, "Contract Name", msg->titleLength); - getEthDisplayableAddress(tmpContent.txContent.destination, - msg->msg, - msg->msgLength, - &global_sha3, - chainConfig->chainId); - msg->result = ETH_PLUGIN_RESULT_OK; - break; - - case 1: - strlcpy(msg->title, "NFT Contract", msg->titleLength); - getEthDisplayableAddress(context->address, - msg->msg, - msg->msgLength, - &global_sha3, - chainConfig->chainId); - msg->result = ETH_PLUGIN_RESULT_OK; - break; - - case 2: - strlcpy(msg->title, "TokenID", msg->titleLength); - snprintf(msg->msg, 70, "0x%.*H", 32, context->tokenId); - msg->result = ETH_PLUGIN_RESULT_OK; - break; - - default: - break; - } + handle_query_contract_ui(parameters); } break; - default: PRINTF("Unhandled message %d\n", message); + break; } } diff --git a/src_plugins/erc721/erc721_plugin.h b/src_plugins/erc721/erc721_plugin.h new file mode 100644 index 0000000..235b585 --- /dev/null +++ b/src_plugins/erc721/erc721_plugin.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include "eth_plugin_handler.h" +#include "shared_context.h" +#include "ethUtils.h" +#include "utils.h" + +// Internal plugin for EIP 721: https://eips.ethereum.org/EIPS/eip-721 + +#define NUM_ERC721_SELECTORS 5 + +typedef enum { + APPROVE, + SET_APPROVAL_FOR_ALL, + TRANSFER, + SAFE_TRANSFER, + SAFE_TRANSFER_DATA, +} erc721_selector_t; + +typedef enum { + FROM, + TO, + DATA, + TOKEN_ID, + OPERATOR, + APPROVED, + NONE, +} erc721_selector_field; + +typedef struct erc721_context_t { + uint8_t address[ADDRESS_LENGTH]; + uint8_t tokenId[INT256_LENGTH]; + + bool approved; + + erc721_selector_field next_param; + uint8_t selectorIndex; +} erc721_context_t; + +void handle_provide_parameter(void *parameters); +void handle_query_contract_ui(void *parameters); \ No newline at end of file diff --git a/src_plugins/erc721/erc721_provide_parameters.c b/src_plugins/erc721/erc721_provide_parameters.c new file mode 100644 index 0000000..76e1ea5 --- /dev/null +++ b/src_plugins/erc721/erc721_provide_parameters.c @@ -0,0 +1,93 @@ +#include "erc721_plugin.h" +#include "eth_plugin_internal.h" + +static void handle_approve(ethPluginProvideParameter_t *msg, erc721_context_t *context) { + switch (context->next_param) { + case OPERATOR: + copy_address(context->address, msg->parameter, sizeof(context->address)); + context->next_param = TOKEN_ID; + break; + case TOKEN_ID: + copy_parameter(context->tokenId, msg->parameter, sizeof(context->tokenId)); + context->next_param = NONE; + break; + default: + PRINTF("Unhandled parameter offset\n"); + msg->result = ETH_PLUGIN_RESULT_ERROR; + break; + } +} + +// `strict` will set msg->result to ERROR if parsing continues after `TOKEN_ID` has been parsed. +static void handle_transfer(ethPluginProvideParameter_t *msg, + erc721_context_t *context, + bool strict) { + switch (context->next_param) { + case FROM: + context->next_param = TO; + break; + case TO: + copy_address(context->address, msg->parameter, sizeof(context->address)); + context->next_param = TOKEN_ID; + break; + case TOKEN_ID: + copy_parameter(context->tokenId, msg->parameter, sizeof(context->tokenId)); + context->next_param = NONE; + break; + default: + if (strict) { + PRINTF("Param %d not supported\n", context->next_param); + msg->result = ETH_PLUGIN_RESULT_ERROR; + } + break; + } +} + +static void handle_approval_for_all(ethPluginProvideParameter_t *msg, erc721_context_t *context) { + switch (context->next_param) { + case OPERATOR: + context->next_param = APPROVED; + copy_address(context->address, msg->parameter, sizeof(context->address)); + break; + case APPROVED: + context->next_param = NONE; + context->approved = msg->parameter[PARAMETER_LENGTH - 1]; + break; + default: + PRINTF("Param %d not supported\n", context->next_param); + msg->result = ETH_PLUGIN_RESULT_ERROR; + break; + } +} + +void handle_provide_parameter(void *parameters) { + ethPluginProvideParameter_t *msg = (ethPluginProvideParameter_t *) parameters; + erc721_context_t *context = (erc721_context_t *) msg->pluginContext; + + PRINTF("erc721 plugin provide parameter %d %.*H\n", + msg->parameterOffset, + PARAMETER_LENGTH, + msg->parameter); + + msg->result = ETH_PLUGIN_RESULT_SUCCESSFUL; + switch (context->selectorIndex) { + case APPROVE: + handle_approve(msg, context); + break; + case SAFE_TRANSFER: + case TRANSFER: + handle_transfer(msg, context, true); + break; + case SAFE_TRANSFER_DATA: + // Set `strict` to `false` because additional data might be present. + handle_transfer(msg, context, false); + break; + case SET_APPROVAL_FOR_ALL: + handle_approval_for_all(msg, context); + break; + default: + PRINTF("Selector index %d not supported\n", context->selectorIndex); + msg->result = ETH_PLUGIN_RESULT_ERROR; + break; + } +} \ No newline at end of file diff --git a/src_plugins/erc721/erc721_ui.c b/src_plugins/erc721/erc721_ui.c new file mode 100644 index 0000000..fefa3cf --- /dev/null +++ b/src_plugins/erc721/erc721_ui.c @@ -0,0 +1,166 @@ +#include "erc721_plugin.h" + +static void set_approval_ui(ethQueryContractUI_t *msg, erc721_context_t *context) { + switch (msg->screenIndex) { + case 0: + strlcpy(msg->title, "Allow", msg->titleLength); + getEthDisplayableAddress(context->address, + msg->msg, + msg->msgLength, + &global_sha3, + chainConfig->chainId); + break; + case 1: + strlcpy(msg->title, "To Spend Your", msg->titleLength); + if (msg->item1) { + strlcpy(msg->msg, (const char *) &msg->item1->nft.collectionName, msg->msgLength); + } else { + strlcpy(msg->msg, "Not found", msg->msgLength); + } + break; + case 2: + strlcpy(msg->title, "NFT Address", msg->titleLength); + getEthDisplayableAddress(msg->pluginSharedRO->txContent->destination, + msg->msg, + msg->msgLength, + &global_sha3, + chainConfig->chainId); + break; + case 3: + strlcpy(msg->title, "NFT ID", msg->titleLength); + uint256_to_decimal(context->tokenId, + sizeof(context->tokenId), + msg->msg, + msg->msgLength); + break; + case 4: + strlcpy(msg->title, "And send", msg->titleLength); + amountToString((uint8_t *) &msg->pluginSharedRO->txContent->value, + sizeof(msg->pluginSharedRO->txContent->value), + WEI_TO_ETHER, + msg->network_ticker, + msg->msg, + msg->msgLength); + default: + PRINTF("Unsupported screen index %d\n", msg->screenIndex); + msg->result = ETH_PLUGIN_RESULT_ERROR; + break; + } +} + +static void set_approval_for_all_ui(ethQueryContractUI_t *msg, erc721_context_t *context) { + switch (msg->screenIndex) { + case 0: + if (context->approved) { + strlcpy(msg->title, "Allow", msg->titleLength); + } else { + strlcpy(msg->title, "Revoke", msg->titleLength); + } + getEthDisplayableAddress(context->address, + msg->msg, + msg->msgLength, + &global_sha3, + chainConfig->chainId); + break; + case 1: + strlcpy(msg->title, "To Manage ALL", msg->titleLength); + if (msg->item1) { + strlcpy(msg->msg, (const char *) &msg->item1->nft.collectionName, msg->msgLength); + } else { + strlcpy(msg->msg, "Not found", msg->msgLength); + } + break; + case 2: + strlcpy(msg->title, "NFT Address", msg->titleLength); + getEthDisplayableAddress(msg->pluginSharedRO->txContent->destination, + msg->msg, + msg->msgLength, + &global_sha3, + chainConfig->chainId); + break; + case 3: + strlcpy(msg->title, "And send", msg->titleLength); + amountToString((uint8_t *) &msg->pluginSharedRO->txContent->value, + sizeof(msg->pluginSharedRO->txContent->value), + WEI_TO_ETHER, + msg->network_ticker, + msg->msg, + msg->msgLength); + default: + PRINTF("Unsupported screen index %d\n", msg->screenIndex); + msg->result = ETH_PLUGIN_RESULT_ERROR; + break; + } +} + +static void set_transfer_ui(ethQueryContractUI_t *msg, erc721_context_t *context) { + switch (msg->screenIndex) { + case 0: + strlcpy(msg->title, "To", msg->titleLength); + getEthDisplayableAddress(context->address, + msg->msg, + msg->msgLength, + &global_sha3, + chainConfig->chainId); + break; + case 1: + strlcpy(msg->title, "Collection Name", msg->titleLength); + if (msg->item1) { + strlcpy(msg->msg, (const char *) &msg->item1->nft.collectionName, msg->msgLength); + } else { + strlcpy(msg->msg, "Not Found", msg->msgLength); + } + break; + case 2: + strlcpy(msg->title, "NFT Address", msg->titleLength); + getEthDisplayableAddress(msg->pluginSharedRO->txContent->destination, + msg->msg, + msg->msgLength, + &global_sha3, + chainConfig->chainId); + break; + case 3: + strlcpy(msg->title, "NFT ID", msg->titleLength); + uint256_to_decimal(context->tokenId, + sizeof(context->tokenId), + msg->msg, + msg->msgLength); + break; + case 4: + strlcpy(msg->title, "And send", msg->titleLength); + amountToString((uint8_t *) &msg->pluginSharedRO->txContent->value, + sizeof(msg->pluginSharedRO->txContent->value), + WEI_TO_ETHER, + msg->network_ticker, + msg->msg, + msg->msgLength); + default: + PRINTF("Unsupported screen index %d\n", msg->screenIndex); + msg->result = ETH_PLUGIN_RESULT_ERROR; + break; + } +} + +void handle_query_contract_ui(void *parameters) { + ethQueryContractUI_t *msg = (ethQueryContractUI_t *) parameters; + erc721_context_t *context = (erc721_context_t *) msg->pluginContext; + + msg->result = ETH_PLUGIN_RESULT_OK; + switch (context->selectorIndex) { + case APPROVE: + set_approval_ui(msg, context); + break; + case SET_APPROVAL_FOR_ALL: + set_approval_for_all_ui(msg, context); + break; + case SAFE_TRANSFER_DATA: + case SAFE_TRANSFER: + case TRANSFER: + set_transfer_ui(msg, context); + break; + default: + msg->result = ETH_PLUGIN_RESULT_ERROR; + PRINTF("Unsupported selector index %d\n", context->selectorIndex); + break; + } +} \ No newline at end of file diff --git a/src_plugins/starkware/starkware_plugin.c b/src_plugins/starkware/starkware_plugin.c index 31abfb5..f4ba5f1 100644 --- a/src_plugins/starkware/starkware_plugin.c +++ b/src_plugins/starkware/starkware_plugin.c @@ -188,8 +188,9 @@ bool starkware_verify_asset_id(uint8_t *tmp32, uint8_t *tokenId, bool assetTypeO if (quantumSet) { cx_sha3_t sha3; tokenDefinition_t *currentToken = NULL; - if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) { - currentToken = &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex]; + if (dataContext.tokenContext.quantumIndex != MAX_ITEMS) { + currentToken = + &tmpCtx.transactionContext.extraInfo[dataContext.tokenContext.quantumIndex].token; } cx_keccak_init(&sha3, 256); compute_token_id(&sha3, @@ -214,9 +215,9 @@ bool starkware_verify_asset_id(uint8_t *tmp32, uint8_t *tokenId, bool assetTypeO bool starkware_verify_token(uint8_t *token) { if (quantumSet) { - if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) { + if (dataContext.tokenContext.quantumIndex != MAX_ITEMS) { tokenDefinition_t *currentToken = - &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex]; + &tmpCtx.transactionContext.extraInfo[dataContext.tokenContext.quantumIndex].token; if (memcmp(token + 32 - 20, currentToken->address, 20) != 0) { PRINTF("Token not matching got %.*H\n", 20, token + 32 - 20); PRINTF("Current token %.*H\n", 20, currentToken->address); @@ -235,7 +236,7 @@ bool starkware_verify_token(uint8_t *token) { bool starkware_verify_quantum(uint8_t *quantum) { if (quantumSet) { - if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) { + if (dataContext.tokenContext.quantumIndex != MAX_ITEMS) { if (memcmp(quantum, dataContext.tokenContext.quantum, 32) != 0) { PRINTF("Quantum not matching got %.*H\n", 32, quantum); PRINTF("Current quantum %.*H\n", 32, dataContext.tokenContext.quantum); @@ -301,7 +302,7 @@ void starkware_print_amount(uint8_t *amountData, char *ticker = chainConfig->coinName; if ((amountData == NULL) || - (forEscape && (dataContext.tokenContext.quantumIndex == MAX_TOKEN))) { + (forEscape && (dataContext.tokenContext.quantumIndex == MAX_ITEMS))) { decimals = WEI_TO_ETHER; if (!forEscape) { convertUint256BE(tmpContent.txContent.value.value, @@ -312,7 +313,7 @@ void starkware_print_amount(uint8_t *amountData, } } else { tokenDefinition_t *token = - &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex]; + &tmpCtx.transactionContext.extraInfo[dataContext.tokenContext.quantumIndex].token; decimals = token->decimals; ticker = token->ticker; readu256BE(amountData, &amountPre); @@ -334,9 +335,9 @@ void starkware_print_amount(uint8_t *amountData, void starkware_print_ticker(char *destination, size_t destinationLength) { char *ticker = chainConfig->coinName; - if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) { + if (dataContext.tokenContext.quantumIndex != MAX_ITEMS) { tokenDefinition_t *token = - &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex]; + &tmpCtx.transactionContext.extraInfo[dataContext.tokenContext.quantumIndex].token; ticker = token->ticker; } strlcpy(destination, ticker, destinationLength); @@ -345,9 +346,9 @@ void starkware_print_ticker(char *destination, size_t destinationLength) { // TODO : rewrite as independant code void starkware_print_asset_contract(char *destination, size_t destinationLength) { // token has been validated to be present previously - if (dataContext.tokenContext.quantumIndex != MAX_TOKEN) { + if (dataContext.tokenContext.quantumIndex != MAX_ITEMS) { tokenDefinition_t *token = - &tmpCtx.transactionContext.tokens[dataContext.tokenContext.quantumIndex]; + &tmpCtx.transactionContext.extraInfo[dataContext.tokenContext.quantumIndex].token; getEthDisplayableAddress(token->address, destination, destinationLength, diff --git a/tests/build_local_test_elfs.sh b/tests/build_local_test_elfs.sh index dd84b45..546690f 100755 --- a/tests/build_local_test_elfs.sh +++ b/tests/build_local_test_elfs.sh @@ -1,11 +1,11 @@ #!/bin/bash # FILL THESE WITH YOUR OWN SDKs PATHS -# NANOS_SDK= -# NANOX_SDK= +NANOS_SDK=$TWO +NANOX_SDK=$X # list of apps required by tests that we want to build here -appnames=("ethereum" "ethereum_classic") +appnames=("ethereum") # create elfs folder if it doesn't exist mkdir -p elfs @@ -18,17 +18,17 @@ for app in "${appnames[@]}" do echo "**Building $app for Nano S..." make clean BOLOS_SDK=$NANOS_SDK - make -j DEBUG=1 ALLOW_DATA=1 BOLOS_SDK=$NANOS_SDK CHAIN=$app + make -j ALLOW_DATA=1 NFT_TESTING_KEY=1 DEBUG=1 BOLOS_SDK=$NANOS_SDK CHAIN=$app cp bin/app.elf "tests/elfs/${app}_nanos.elf" done -echo "*Building elfs for Nano X..." -for app in "${appnames[@]}" -do - echo "**Building $app for Nano X..." - make clean BOLOS_SDK=$NANOX_SDK - make -j DEBUG=1 ALLOW_DATA=1 BOLOS_SDK=$NANOX_SDK CHAIN=$app - cp bin/app.elf "tests/elfs/${app}_nanox.elf" -done +# echo "*Building elfs for Nano X..." +# for app in "${appnames[@]}" +# do +# echo "**Building $app for Nano X..." +# make clean BOLOS_SDK=$NANOX_SDK +# make -j DEBUG=1 BOLOS_SDK=$NANOX_SDK CHAIN=$app +# cp bin/app.elf "tests/elfs/${app}_nanox.elf" +# done echo "done" diff --git a/tests/snapshots/nanos_erc721_approval_for_all/00000.png b/tests/snapshots/nanos_erc721_approval_for_all/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..2994983f84cf76ab587bac356d5afe297c0e0632 GIT binary patch literal 541 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfcyxba4!+V0?SfW}LV) zm52AlpJ_b2>@)W(0ade0N=>L#7k{_%p*K+Izf)VA1-(C=WRZ-BymEd z+ycg#=gr>vKI=O3DZ$93W?LaYQ1QY>pxO%?Y&v*&<#TnOD>)zbXF6qTB9pT3N&!&M z3dWi9&EBnj=ymuLi`0al9u7OFx80vqnaz8`W+_{Wop8XrwTVif&jYR3;ytm)i1F!7 zQ=gO!i4>qCfqpe|KCDm-Wb*LtR{=T`$TJ28kKEHk+7p0Y?mPqLwy~wK0ofM}OcabW z9iDmf0+pZOF#@XZFL)Sha%TzS%!Lnq%!RyR`IX+l PXlL+r^>bP0l+XkKp5EPW literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_approval_for_all/00001.png b/tests/snapshots/nanos_erc721_approval_for_all/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..ef35bb6b074fc8ce4dd6e92d57018616de758cbd GIT binary patch literal 444 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfcZIx;TbZFupw)*!9GN!TI3w zBfI`LeqCsqc5BM&s))9Eu|3AhNA&M{Ce7^LmApK*d{V?S#RYF}EdsL)DlW>N4q=(H zJj>zPBF34{huI~i>`vHRJa*Ys;d8W+d@$y8w#|@PQfk7_3k8bC zlY4KTJdE-$MQxMMEM%M+%xturEhS^e1WS{c6EP-pfR0n2X??3=7F&uR?+FEGW+R{$ z5K%nA(lqA894Vkeh2o11XBIwOXZCIltl^IiKUQ3vH3mMs8 Xb)8x4E4;V|7%L2(u6{1-oD!MAA_XyOni9b7bPxjY+EhW#wL{!ky#PSy?za&q*Pr9vr)LIg7FE3W**-E z&8_8^9hiB84|@S&tkL_dhjUGU5=JH<#Czg`fr-NAOATk@GZ!$<^lWPj-|}IV)C9?t zj0#KM6KhT=I3NE1;DC1ajoP)$>wVI)D!PDrfI1a6r?STdG8<_FnZ=WNcqgBUpSX~5 z=4`16k(o16ma(P8O>8Jy$2ikB+u_;#42Ng#rV7P*4`Y~(cAG0ZAGTzCx`c7&U1^|X z%0m!q_T+^RQy#jswcVW!7V|eUd2G|ezAUxxQset;1}7DqfuX^BA|_)-irtdLNx%@40NJ*zZ7N$z z9?-~jY$Nb#(dL{~=@ABC!2Kx)>we8BFSX2QK&b%l7v@pvnIv-Z} ztO$%5kfT+c533lP)IfV9dY7_Ikp$C?+bM-cv(2d`=0uy3N{eG``DGrV3*Yy>&-1*``}NgSm!pYz zqC_G=D=YN15NTM7<5JkaXW4Tkl4D$@zO>GPY|j}x!`Dvf=QO!r57J8HDNEzOhLuEe zesOBm%Ny}=ljq!Q>TZ8t)9&SD-?DO~@m-l|*7Iqzur)qndfMDicTy<7MJoKHA{H)$ zAY)IXd{9jp5{U9U>pborMc9sP4erKJ{w(1|SZ~--gpxru-VLL9@w3LJm12~&0VX^) z+q4!R!m4Z|2&>d_C)+aW6Nra0^v49khG@YhBgi;fka3+e17T}H1}j+ia|qz)gS6no z-93gh*BHWPVz~RyRRYwg20=hbO<4ig%nwK@SB19cEy6xtqJ<7XoW8A%%sTgQ_xTGL zhOed6tdWYY-diaDVa2gP3!8yJFlyOw%23~#WmZF1Lj!w zv&BYw*9}|~wC`ww+c6~@ik^T@U)=a;gxqH$^e`i4Xd^y;HOEhuyjl-K#*6pSY0rvH z8xhqQn;iN>NfwcO3+hx;2PX7xK>RNpis7kZg<|hY83ufiu@7K{qPfV=ovET~RolkS c{Qs~mclOoWGxO@l;8#m3%c}Jgmu}ww3zQT`iU0rr literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_approval_for_all/00004.png b/tests/snapshots/nanos_erc721_approval_for_all/00004.png new file mode 100644 index 0000000000000000000000000000000000000000..c6051dd1651ad0b66d2619bb2dbf05dece07240e GIT binary patch literal 540 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfcy$ba4!+V0?SD~ef$(ikY(f2fr zWr}gO!?Oj9GZ!+>v|k6L5-0t1Wi~SLF$!ijO8@fUjMN06jJ&e3sR9U{@Zmi%=kBqq zCKntIUn#f%gyGWRml}ACPFyemA`=DY!xtLPI5Hct^O`7p4rAs$;bUYn=i;$QklqU) z7N<;Dn3(18%z;f(%I;vZadyPNb?oVC#-@MtjZ8o)8qUnMwOGuS!k3ca@GKhSjQ^cH zyeD{gC!YbCdsqeNh+;RUQ>)oh^40=fya?#Fs|{yX_lEPH&=FU5KCI37bS2}=r-!5_ zRNkt{a!A{0At^P_Y+g!6gdWg$%X`;@SgROku4kD7)Kb&Z#ticEOewyUwl>da3lb+t z0-1j68E1wwom!qHk#Y}c2GA*M*;3kou9BLtv#Hl#{quDb9$u4}i_O+qGg6kdIi`Wa zC?4q8`L7OM1v*gSbF`5&v(bH^xpp^?O*K{6T$BI!vgw~NW~0;9{B|>ent|a9@{Es> z^I?_GVL*J6Wy;fSX49f?N95%u@7&t^^cs-!o4r!@j-{+MPZluN89ZJ6T-G@yGywqh CkKJzo literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_approval_for_all/00005.png b/tests/snapshots/nanos_erc721_approval_for_all/00005.png new file mode 100644 index 0000000000000000000000000000000000000000..653e23c999c363bd888f788d2e5206cd238f83d3 GIT binary patch literal 630 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfiGAx;TbZFupw)7$oh<;Bqj$ zYvphG9d@UR+FveUJ>6tzaLH%ckHz7WEM&W@j!s=yrP_J9kV7Lp_hRUJl|rjF?(<)| zE-&QJh+q4VbtZqNM2Z-1^BFx;h0Pfb&sy11_FZm$uh4u3h!jl}oDaw4zHvOP@>!FY zd*;GKMPpN)l(sf;AohF~m-}Z6Tgo!FlsHvqW+Nan@i7WM?39+}kTzkalo&_^gbv%@ zt}x-1zxE-KHy);nOn8jePm+|n=bM%h0c3N}{C{;rx%BewC;ynvT$$K! zRyT`>ck-F#rZo|VS4*kQbOc(NcCq1%QyUx5_SQ2OHca^G#dK;dTMFM=#+k0nM&&CR zXGV+XnJN@l85o(|5ek@RqEP&6aiU`Jcc}@LN(=s6YdG_gL2AO!w}*r!rTqBUG0s#M zpEr5o!!HYf^6w7mN=ntm9NwP&V@liIEQe>;TCbn@vw|%pqhfp8W$Bc-&4vC_KyOW0 zn3y2}@_t%Yg(JwT7aMqhLF0N@#n{xxsCwf^j+6`skT?(leK#TU#D#`4Km?RlFh1$S zdjhDaI5A&pf+Wyn3Et|>8{K*1*CkH)2@bF7jT_y8VK{w4;YP|h;gQS;lq*)X?JC3U}Ri0 oIALSO__QQK+^@Y1D7>FR%=cI8$`9Va>FVdQ&MBb@0F*HPPXGV_ literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_approval_for_all/00006.png b/tests/snapshots/nanos_erc721_approval_for_all/00006.png new file mode 100644 index 0000000000000000000000000000000000000000..b5efb214648fa4fef42b3ad2da409ccebee7fc99 GIT binary patch literal 835 zcmXYwe@GKy7{|93m)V_Bn6^u5rY;dFIX4y3noe^=QEM4WX0DETDwH@%C8?QpqGE0{ z)Wfhg%P>oYK_H4CM{&{>^H&bbKZII8&><-)McMOe+>htp_nv#6&+~nrYskq;C;Ul& zi9|x^b*X2-THsRnVQ`;2Tpl5j$o%!G$!DviOJnCNqVy8&SlG6uTVuc65mB%A9gX)C z@$1ROjT@_}dxyU`yULxdUAjwaiQDhDzWn<5h~1M^FB@3BGjG_FDa6CKWB^vJw8QS@MnP#doG}RLp(YL!y<9$^dObCmA6XaxJNL8qo_qA+p&eDwM&uKy! zQ9c@(v52R5h1s>GqKOcqLB(EUXsjBi9!|!L7eGEI6bvGO=;HLCH6LntZ!^x-|@B(33jJ~3b1)7OL-~ffg&21uJ>b-|K^Szo}kWxk&c+?VOx6uyav^=lM zq*T##|D**cPEOd;k@R`czH>P%`j;YTi|!l}xs z<#Iy6387%R+=epGka2Vm+bMkfE>tr65uB21W*tVD7cC*wG>GnH$L12DkG`=Dl%hJk zTB-@!9PWb0bi}tZ6*AO@6Ln%e&8!EaAwkU%YIH1c02a(m+QJjxvetnXC~2NepOH0NZMG-2!0MVD3OamHzN#JXep*W7N+%i~WgsxC$^ zCK>O9%PcA1HY%243$850nkO@3u(gq+CqK2PRNnTtp8kHb|E_9qr5I-tNHLYvGwf%v zV#z7B(XNa!OlYey*zEC)o)qWOxHCBO<4SN3?egW>Og6hAWCYkL{Y4UQu(3+7*hz3n zYob~P9piyvn{B^5em~qBqWtCbwU9OhzN75~+oD^3^tlbpDDjPMC zNx2B}_`z1Bu^VjNLxE(PU$!sX!##wYlE8>_U-L{`givp9Iw9;AMlm5*o~4)+|HMTm znGu@k%_4*s*$9yoeF~^pE(hm1{z(VHrE|Q2py<~x{=#-nGDYuMf0GimOHu`5Z zZ=I4GcR+)+{%S+W5;1d~6gv?KtxPsHmD#6LHkRUY@Pg0-Z=4bzFYmPURgejeVZxZM zR&2fxddOXai9pc{)A0s`+-$QT@h8UQQcCY)L!ZPiyL>w#1FC`i1C7BV?pSrW5C<38 zl?NC_KOceR%dl0f1-c!edflbqd7unCi`+H+zs>Ux=hHZN_uaF0zXQg@;20p=#)1oX zK_|u&C^e0PJloDWy1?^LHIt7sWxX(Iv``Ph1D@CH|7oWC(7aQ6%%+kaOo-8KhXYyi zMe+o+rk1@z7r{M0YlDxLSGAy}JJARFzvIJuDY{N&^yiYt;p~Nlh9bX$Cfg09plV}iIZ+-IJC8Sre!%io2X~w z%xok->E;AUsR=(rn2k&nHcJ}?GlNj`nJW!vHpf)3fuvqb*uF1z|8yK z8)z;Mum44d!)`!0{iK5PVW2HtJVquqms{%{pG8Z}^GkD1%Wy~ox#;HX*7QjWA3jW( z;P`C*goTMfbN251kONfg0`wu!u|Tul0~L6uAeblgY!Xm z$36cWe;(xOj_SzSo^k7n=aRXC_NB_5FQcyTO6TsHc%(#@>C`gaTO~jtDY1Jm+Fk~+ zObO4HNa2%Wn^}MDgTu4KrV5*Zd^r;Z=fi$!*Bj1gubngVjJGL}+su~2HZzvlXnyKb z?QDw)l2Ra4IlXP}nRSWk#wKr4>b#%rj{KoxWb#I8g5?B9wwe5w8+eS^c~2;Oc3_(c zr2kB1lbUezzNA^)r%FJ znSmxYoNTrZa7m? zWMpzj=gx@>26JPWjm)|8PXbv$SLK5Z`T0$XFQu)G-3Vv{&?hz7yeIBNSGqkjfw(ND z>5MV=<)v%q#2)s0)^8GX^wiChdu9XO5vl7leVak-ty4Em-=r*4W=!3+ruF3|v1Q)T jGxTy3cWnW3>KM-WtzW%&o?Ip{Vi`PL{an^LB{Ts5TJzP~ literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_approval_for_all/00010.png b/tests/snapshots/nanos_erc721_approval_for_all/00010.png new file mode 100644 index 0000000000000000000000000000000000000000..595591af309af11f2e49002000011c034c890690 GIT binary patch literal 798 zcmZ{iZAcSw9LG0DqjYCTPwo;sOTm{_A@ZYgF?Vui?IaN<+m zGKoWrM5Y;}0}~V_uoF7tPi-n?Vag2mD$|-T@{RVJy$=3&fA07_eZJpcOMY%P7K(>T zBofSUIrA#q3_QesIlRBd*2YUDA!~-rjB5_*`gB2!$#9sOrZSdxo3^JT@b7J}<5gFF zEI!^r_<;8>{EduI!i|OOL@ITLj;OGG*n{8d6zVJR_2oEEP*SzEpIS9;U|GH27_8zOV0&_cTTAGgN2PdU4>FSHX3L$WIJfPR(>q60J93n^uE0hPoL1vpN}5|v zSM)l(4DwCSdNx;Q!jyRU!C@(XOw|}i3BC+15jZJb8TU9#+l_iKTbB?qvCv6;q!JWIiNay(>+%j&g4&&l$roUm0+`D^J>&3rPyXp zPibqjSNlBajJMHzpwbgDXAk}lWHvf+;e(iwNzB>a`Rg8fpS)mjlE(<>bfCLGJG8L@ zwRi$u^2`zH4W?6@*;2OMztC`II#AU9V#Aru&FQ=+a>SLL4;wQ+UB@`{?IEcNJFRzQ zJEZNlkd%7I_bw&lh8|FScXL08wTf|OKi|BQ7dEu8NlgP9W6YT93i9BIH7^pPXEkhE z1@u`c`|2zQHc6?l(ha(A5u!||wykN-&1AoQiErCd*&J`{8`HjV>u%YZs*`v21~57q NJYD@<);T3K0RSY7(dz&J literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_approval_for_all/00012.png b/tests/snapshots/nanos_erc721_approval_for_all/00012.png new file mode 100644 index 0000000000000000000000000000000000000000..3158ea690450f2b8c85c12004b002b02e7b02539 GIT binary patch literal 582 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfj3Yx;TbZFupw)IO&lagUi8i zu2=uq*Yz9Se)!g*VK4JBV-C&WKWoz`S-k14IyUoMm0IWFIu4EW+>4RtRSIvkw{qJ) z-k{BNYMKd;QTzYAL~p5OZSof#4llR(=qVMJ($*$Ab77)uTE-4V=fhVFPUOsN*u=_P zev*f``Ha_Lh0T`?P5_w@bmoG=iJGR?wG$oFmOfn9wp(f$o7BG*4~6I6nAJ9Y(#_4S zx*0Q4Cd`zYu+#gQ-puxsHA;+6txb81;;$K;SOc`)`Eb{n3mcS-P1XR-G*4O9=9zYy zWlA>hi9IogRX%T%Vw>52vEj_tR;Wnvk2E8bnCKl>8qOS+%J|{VJNeAR_7fKhJRN>6 zWt_R(#K%Y*B*bf?@cHzid?OPZ*R-q|Qfe~~OJ_JdGiQ7n#BAiBxx@R}2R5LKwiNib zwJkq!XF{bkuSv~rHmSN0W~1w3?WueMPz z$jMOtJF#s}Y?8nbo644w2lP(7Qu7&Lh$$GG`0$>%lOd6E?`G%PjEacEho#sgfuXFQ zeWOb_8W?6gy!z_Ez}=wqxl_z;<{4*T7^Tcp+APW)4b*$#gTTy%593WQI2=|m1}X&# zr~*UES~!4j_tt_NTU!sW)0HX9eYhc;yFK@KDXZ;sPq04($@Y}pt$UkYuJ7O_vMLEmi+4c;Fw95@=>aR7Nxv47?$ZTZtr_3n+(gx*XR_$~kWo+{1#Gb1SXD%3= zxUfOl`S7wtWoKq1=fesh37M0pZl2)bJ;7rH6tf4a>N|5`!^DM*Gq+1kkOZQXHnx<$ zq^B8D6K1Bx%Ze~iP)OohG zIi_9y5O&xt%^k>mro%ENocDx`AG1-use^*|uq^-bNuC4jCC(rnYh>Cs(MmB?|tDnm{ Hr-UW|v$x^l literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_safe_transfer/00000.png b/tests/snapshots/nanos_erc721_safe_transfer/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..2994983f84cf76ab587bac356d5afe297c0e0632 GIT binary patch literal 541 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfcyxba4!+V0?SfW}LV) zm52AlpJ_b2>@)W(0ade0N=>L#7k{_%p*K+Izf)VA1-(C=WRZ-BymEd z+ycg#=gr>vKI=O3DZ$93W?LaYQ1QY>pxO%?Y&v*&<#TnOD>)zbXF6qTB9pT3N&!&M z3dWi9&EBnj=ymuLi`0al9u7OFx80vqnaz8`W+_{Wop8XrwTVif&jYR3;ytm)i1F!7 zQ=gO!i4>qCfqpe|KCDm-Wb*LtR{=T`$TJ28kKEHk+7p0Y?mPqLwy~wK0ofM}OcabW z9iDmf0+pZOF#@XZFL)Sha%TzS%!Lnq%!RyR`IX+l PXlL+r^>bP0l+XkKp5EPW literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_safe_transfer/00001.png b/tests/snapshots/nanos_erc721_safe_transfer/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..6ff345b740b4a214d00be4aa68764e985e20cd02 GIT binary patch literal 408 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfhh@x;TbZFupx_vG0n3h|9st zVZZ*{|FHYwCYbCiG%a?5!;!M7KNPJ!b&3Pa-fTJTCv@6|r6pDf%#xAH-;_MFtHEHE z$cZ^i9G*>hK*O$na(uRZ`ov^1JyMr zST3=cSUIUTaOn?a%?Uq)x&(82e1P7IPT>P`Rc8vCOtY2@I+Ku`qFLwN<@YSuXvvF| ld0q|?%dSQ&yM~0mF_ySC=ANCH)&UH022WQ%mvv4FO#rmFqe%b& literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_safe_transfer/00002.png b/tests/snapshots/nanos_erc721_safe_transfer/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..795947ce2810997919a4a6465ab9760ea95cc9a5 GIT binary patch literal 693 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfi44x;TbZFupw)*e4Y!;&O0u z$DRL8pOx8dedBjZ-MU~ljW<;AQT^TtM-t<&aO;&^))K& zoh4yZe)7%5ndMg+&SW^GEoPhz4$lsE^7EdMxi^Q0SKhB?!or6io=HvkS@6i+$VBE|2(yvBP=Fm!)v`9% zXNtVtXQr~HtTWL8`r||nm~wu$j4j3Ou#&M<+nEdrBWa+LSRl^0F(FdQEJf~8!9TcoZEl_qzAG97@@pCS1tsF5YJ3k zsCXVQPJw~e)20XtDUfVUG%%iwfJ)psG}`Z40pk%GolxpOL$Q*|G`j@~Ho)Y_;OXk; Jvd$@?2>?8AAHx6u literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_safe_transfer/00003.png b/tests/snapshots/nanos_erc721_safe_transfer/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..ebd34d4d7891cd7fe3ab7f9956d44f782326010d GIT binary patch literal 739 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfgt1ba4!+V0?S2AOvy^w28qQoWIGJOj;Cxuc*i_+jmfs$&nF|?bEE~W{1oIp@n@^lgq>RoRx-{!JbU88 zhm#sU0S!JZo$c`K@S%3L6ux}Fwl;ol`IHQcJv-S_>~w0>oDV}UrHw5` zk5l7&hC|w7#+mIV3dTTWssM5+$eAZ{fX0QJDr~+83?dksE;ZpMNPXgjpJB{KK&3#m z-J-C&ZMUh9k@MlMGx^sWczEsCC1yyZ%)10+YV&Fv#b-%KvCWLP{5Y2_Wmy}@J;1=q zm;q$pU&lC8UeWomg7FEU6`>%v^MI_5Q8hOGGl>W2)JI1)gWS2St+#DA(0L$b{XpG7 z2LpWw45aoGAXQ5qdh?#Q__!OS(FDj-a^7bafdt8NA+h4}@Ktc^@!t{R;5F#@Y>bP0l+XkKKXo~y literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_safe_transfer/00004.png b/tests/snapshots/nanos_erc721_safe_transfer/00004.png new file mode 100644 index 0000000000000000000000000000000000000000..de1b2bc7dadaf87584f0c7db08974b8deee7eb8f GIT binary patch literal 481 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfgw0ba4!+V0?Qpu>bmoj1D!#8ZF*YZw_t}bu| z8JnMy;gELS;N%;r36>c%QhstasP z6*k92FdO+dFK0`c);)RQ!^{sV&WDQ%c~98z?whnQapwxQlzl=0aX@`Ufo znH}g8=9w!KC*3slFR{nJ>5+{K@ z0t^y$XJ9Za0P?jNQ+KXCSd`uT^cuwT(mLC}F~=WYyjW4z_A)T)7(8A5T-G@yGywoW C zgMa?>KV!b0ks~#&c;*=uuawBpNA-Ir9eKHPMR?51RpFB?Z21|VuF~E0YO0Fg`NW5A z>n&_W8B@J^wT8k!wyr(7 zXLaJFpHtdA)3Pd-CQew$ICEv*g|@Oc?q3J|G*g!ii%o31$l6zAa$W;Sv@9HwV8t1WgS(9o%E`q?*b zwYn>RHb`k}t5@2*wRNqj%sd|6{uhU)rDQBfoFti&F(c)lM;p*36BYuwQfxCtC8gA6 z>Q7#nxb)%tlNSt5<~Ta+yxwr;rGwOjo!bl6GtRtx@Wh3J{D)!8M$4NgEqr+GP&ZqO zAG^Bq;qF#(Ba=CN>lkN>b8k<{u#gR5Hrg)s?&O6Juchi@4o^3E1Jv_f?4Hu+)lw4{ zKD;dDCqJR`bgTQNhBH?hczB%;gS-dyvbGV>AKJVpE^JWRe97RX5AWnNaWNK5l2Q|9 zLQqN@TS{0;hC><<0fTMAOh{Pljl40NErm@|if?gZzTTU_!_R=;kykV}QP?al{>>vT z>qg6&_LK~XlzqKCyeHlqY_-jFNOJ|6)z(&S%42i_=-D+u2h=Zx#*IG9lysomyg)Ic zuo=h-)R{ByR%^Pl^Wm;DUWXNmOPEf*%+v*n9eTO)FZ)t6h6&d`l*|SuECx?kKbLh* G2~7Y7wht5l literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_safe_transfer/00006.png b/tests/snapshots/nanos_erc721_safe_transfer/00006.png new file mode 100644 index 0000000000000000000000000000000000000000..b5efb214648fa4fef42b3ad2da409ccebee7fc99 GIT binary patch literal 835 zcmXYwe@GKy7{|93m)V_Bn6^u5rY;dFIX4y3noe^=QEM4WX0DETDwH@%C8?QpqGE0{ z)Wfhg%P>oYK_H4CM{&{>^H&bbKZII8&><-)McMOe+>htp_nv#6&+~nrYskq;C;Ul& zi9|x^b*X2-THsRnVQ`;2Tpl5j$o%!G$!DviOJnCNqVy8&SlG6uTVuc65mB%A9gX)C z@$1ROjT@_}dxyU`yULxdUAjwaiQDhDzWn<5h~1M^FB@3BGjG_FDa6CKWB^vJw8QS@MnP#doG}RLp(YL!y<9$^dObCmA6XaxJNL8qo_qA+p&eDwM&uKy! zQ9c@(v52R5h1s>GqKOcqLB(EUXsjBi9!|!L7eGEI6bvGO=;HLCH6LntZ!^x-|@B(33jJ~3b1)7OL-~ffg&21uJ>b-|K^Szo}kWxk&c+?VOx6uyav^=lM zq*T##|D**cPEOd;k@R`czH>P%`j;YTi|!l}xs z<#Iy6387%R+=epGka2Vm+bMkfE>tr65uB21W*tVD7cC*wG>GnH$L12DkG`=Dl%hJk zTB-@!9PWb0bi}tZ6*AO@6Ln%e&8!EaAwkU%YIH1c02a(m+QJjxvetnXC~2NepOH0NZMG-2!0MVD3OamHzN#JXep*W7N+%i~WgsxC$^ zCK>O9%PcA1HY%243$850nkO@3u(gq+CqK2PRNnTtp8kHb|E_9qr5I-tNHLYvGwf%v zV#z7B(XNa!OlYey*zEC)o)qWOxHCBO<4SN3?egW>Og6hAWCYkL{Y4UQu(3+7*hz3n zYob~P9piyvn{B^5em~qBqWtCbwU9OhzN75~+oD^3^tlbpDDjPMC zNx2B}_`z1Bu^VjNLxE(PU$!sX!##wYlE8>_U-L{`givp9Iw9;AMlm5*o~4)+|HMTm znGu@k%_4*s*$9yoeF~^pE(hm1{z(VHrE|Q2py<~x{=#-nGDYuMf0GimOHu`5Z zZ=I4GcR+)+{%S+W5;1d~6gv?KtxPsHmD#6LHkRUY@Pg0-Z=4bzFYmPURgejeVZxZM zR&2fxddOXai9pc{)A0s`+-$QT@h8UQQcCY)L!ZPiyL>w#1FC`i1C7BV?pSrW5C<38 zl?NC_KOceR%dl0f1-c!edflbqd7unCi`+H+zs>Ux=hHZN_uaF0zXQg@;20p=#)1oX zK_|u&C^e0PJloDWy1?^LHIt7sWxX(Iv``Ph1D@CH|7oWC(7aQ6%%+kaOo-8KhXYyi zMe+o+rk1@z7r{M0YlDxLSGAy}JJARFzvIJuDY{N&^yiYt;p~Nlh9bX$Cfg09plV}iIZ+-IJC8Sre!%io2X~w z%xok->E;AUsR=(rn2k&nHcJ}?GlNj`nJW!vHpf)3fuvqb*uF1z|8yK z8)z;Mum44d!)`!0{iK5PVW2HtJVquqms{%{pG8Z}^GkD1%Wy~ox#;HX*7QjWA3jW( z;P`C*goTMfbN251kONfg0`wu!u|Tul0~L6uAezo~SW_IUQDR;h<5_|Wh z?PUY@GykuB*m>sWTA%-ucFvk=oOxrSq|^i;`Z=ww_sp_HWn+_^l)k)& z!6z$36)18$C2mrs=d;c;7aGo7)QxduHacgc1tr?)mp_CV0WDyYlrl4NKJ51F z1#Hp^{7>=P%RsrQ7z~nXat^#x{eetDnm{r-UW|;#}&` literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_safe_transfer/00010.png b/tests/snapshots/nanos_erc721_safe_transfer/00010.png new file mode 100644 index 0000000000000000000000000000000000000000..3158ea690450f2b8c85c12004b002b02e7b02539 GIT binary patch literal 582 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfj3Yx;TbZFupw)IO&lagUi8i zu2=uq*Yz9Se)!g*VK4JBV-C&WKWoz`S-k14IyUoMm0IWFIu4EW+>4RtRSIvkw{qJ) z-k{BNYMKd;QTzYAL~p5OZSof#4llR(=qVMJ($*$Ab77)uTE-4V=fhVFPUOsN*u=_P zev*f``Ha_Lh0T`?P5_w@bmoG=iJGR?wG$oFmOfn9wp(f$o7BG*4~6I6nAJ9Y(#_4S zx*0Q4Cd`zYu+#gQ-puxsHA;+6txb81;;$K;SOc`)`Eb{n3mcS-P1XR-G*4O9=9zYy zWlA>hi9IogRX%T%Vw>52vEj_tR;Wnvk2E8bnCKl>8qOS+%J|{VJNeAR_7fKhJRN>6 zWt_R(#K%Y*B*bf?@cHzid?OPZ*R-q|Qfe~~OJ_JdGiQ7n#BAiBxx@R}2R5LKwiNib zwJkq!XF{bkuSv~rHmSN0W~1w3?WueMPz z$jMOtJF#s}Y?8nbo644w2lP(7Qu7&Lh$$GG`0$>%lOd6E?`G%PjEacEho#sgfuXFQ zeWOb_8W?6gy!z_Ez}=wqxl_z;<{4*T7^Tcp+APW)4b*$#gTTy%593WQI2=|m1}X&# zr~*UES~!4j_tt_NTU!sW)0HX9eYhc;yFK@KDXZ;sPq04($@Y}pt$UkYuJ7O_vMLEmi+4c;Fw95@=>aR7Nxv47?$ZTZtr_3n+(gx*XR_$~kWo+{1#Gb1SXD%3= zxUfOl`S7wtWoKq1=fesh37M0pZl2)bJ;7rH6tf4a>N|5`!^DM*Gq+1kkOZQXHnx<$ zq^B8D6K1Bx%Ze~iP)OohG zIi_9y5O&xt%^k>mro%ENocDx`AG1-use^*|uq^-bNuC4jCC(rnYh>Cs(MmB?|tDnm{ Hr-UW|v$x^l literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer/00000.png b/tests/snapshots/nanos_erc721_transfer/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..2994983f84cf76ab587bac356d5afe297c0e0632 GIT binary patch literal 541 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfcyxba4!+V0?SfW}LV) zm52AlpJ_b2>@)W(0ade0N=>L#7k{_%p*K+Izf)VA1-(C=WRZ-BymEd z+ycg#=gr>vKI=O3DZ$93W?LaYQ1QY>pxO%?Y&v*&<#TnOD>)zbXF6qTB9pT3N&!&M z3dWi9&EBnj=ymuLi`0al9u7OFx80vqnaz8`W+_{Wop8XrwTVif&jYR3;ytm)i1F!7 zQ=gO!i4>qCfqpe|KCDm-Wb*LtR{=T`$TJ28kKEHk+7p0Y?mPqLwy~wK0ofM}OcabW z9iDmf0+pZOF#@XZFL)Sha%TzS%!Lnq%!RyR`IX+l PXlL+r^>bP0l+XkKp5EPW literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer/00001.png b/tests/snapshots/nanos_erc721_transfer/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..6ff345b740b4a214d00be4aa68764e985e20cd02 GIT binary patch literal 408 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfhh@x;TbZFupx_vG0n3h|9st zVZZ*{|FHYwCYbCiG%a?5!;!M7KNPJ!b&3Pa-fTJTCv@6|r6pDf%#xAH-;_MFtHEHE z$cZ^i9G*>hK*O$na(uRZ`ov^1JyMr zST3=cSUIUTaOn?a%?Uq)x&(82e1P7IPT>P`Rc8vCOtY2@I+Ku`qFLwN<@YSuXvvF| ld0q|?%dSQ&yM~0mF_ySC=ANCH)&UH022WQ%mvv4FO#rmFqe%b& literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer/00002.png b/tests/snapshots/nanos_erc721_transfer/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..36ec626b410aa243eadc783ac3fd5ffb3c2a58dd GIT binary patch literal 688 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFff&Sx;TbZFupw)nD;1(!R6p( zvAzFUzq|AAs){)}!^Y!~26NfeAO7c63U7#i?J=JF)kmoPuRy@LTZ=Z&^L(^{pIiP* z&*60(8tYdiIzRK4nlMv}ZDuc9O2&+oWo#*NXFJ1HjZG9jC-a`Tu;F6E83kigh0mw_ z_N;F6e5Ng| zrDQlf3+6qs#>B@cnAvE(o5Rj)4QFm=J3P~rn(#B-RG~QcVHC4byScLSVN=GZix_7f zmIg{DJp{34PhR*i>7i3w+u_+@F=a_9zWh~;Gy7SlWb>Z5bH(7qovRxZKd)p|) zmQv@+Y~&A&C|{t0#Vk|S15E~63yhYwHr|>3z-YN-aPooy&=hT;g03^bQ1byg;)=n^ zHDLE>8#y0VF*aQT6xB8|QP>PL$(HF<;&w5<-P;OoY;8TfPFJQZ7Z|Da3<0qUWyjUt R3IkIjgQu&X%Q~loCIHci8(9DV literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer/00003.png b/tests/snapshots/nanos_erc721_transfer/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..b656cdff301084d3d91fc5796c44c5723913bfa8 GIT binary patch literal 698 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfcWGx;TbZFupw)SR@@T;(V}K zGwDC~G4?}Mo$G9+ZcS0TojU2V?!U|0o^#^5tBy`xSEbr{`L#g6x?77@$9X>bB4xJk zvbLYTt3#y=FT2tFOATi(8Km$@O<0(CqK7R-?rf{O>Shy#X>8E0N>IHP1NHK8&^ zd|v3`36^)u9!i=j00nMlIy|e-j7VwY;XPq9srPamGf1_pWJ*RwiS+V(pbk~(<@Kft zpR@FAlqIFw&NKoQ_OvNFGaFg+I;R1R&tRGI9jL5|_rxC|Al?dO=Re-dmg48{*4Fl& zWr{iPi8=Ekn2qwy?wKeQR}~qV?73N??0op;Nua4|kE@ML=FAIWHo7kya4*{-EmHz$ zZuglCiwQp$wz;O6$eg%fkm7cDf+Wy~CT~vMxzcbZ!{OPoHs+ZZ3luh+=s;YdV`LAJ z*vq~B1P{owpulK2GvVh1wiKXQmO%X{bU;2+ES`ME-?V1d89!zt=VuETXD$V!X10`R zY{0PNOUb+WW3p7ng2ahGwM`X@gPD0v{!Epc@N)&X{KX9?cqFC7X6^=ZHv!$Y9H?eO zn<6j}e=35k%)e}q@-EBanXz6?3)Fs~6Ebd0{JEt~cBX4urbF5Z9Z=u_L)Y!F{N)YG zn@#ShY&Lmg@<&U&j(w*7GM(C>74Bv3&a0!m3Q^ f<Y0w#W<@%`Lny!1W75indZDFE*PBj;XN_u?77I3JiI6Vq#Fe@gHZFCD-CC6zKsbz ztg^XgTd^ze37{$&6NTan1}FXm9abyS&qw{})V_Vxw#+l5LDH#^Kd!Jr2n-+b0 zrxMt3BNGK<6CWdIW+Qgq$sqG?a%ilVny_;ZTgpFipj{EXC-&T`xY}^0$k51Sk5Iro zsR@yKYZzxP?_JK8GOc^^!iSlUJ=@xzK9ZVHdFzK4khMr^!cQ-UN}!hh%LNLb13@M? zpK$~_$5aOBKrr*OvZ=!7tmncr<-MOxm%0aX0}KI`0KEou4+Powol%z#ztnK%)9q|BH)I`}0Nf3KmVC=ke%+4a=0t+g>bDpJZXv-WpzY z=;2BZjq0o$6MhCT8#yx@*PJ?v9MUpwoIbTT^G7hq(8UjrOMTl` zl%Mjh?aYM_x<=K}H=++8p1km(+}byuZER9%GacL7JfArmnfMqrgRHw?a3bf#1%s1+ zX7KPz^WIl;KCH5to$+Z5vr#+yrG_&*TZ?&5$jJ5Z@IDud%XWD7VL?hpMeajakhHq< z;qF#(BNG`pplW@wIG`;b7D!E~^mVYj4hoB$6E)tePoFpk_2K1ykIG})D1o?8pOrY~^ z*ZxW6J>dfiFL1B}NqKM>0A(l4Olf0F+2;ccqO?gnTc<{6&Pb_q1$yY>hvmSS**-OT z;?7-dj?aQq?x}uG3FY%}kg=A5{r%=q-f j7BN2m+=m~o{9`OD<=Mer>@gXb02w@8{an^LB{Ts5=zt|t literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer/00006.png b/tests/snapshots/nanos_erc721_transfer/00006.png new file mode 100644 index 0000000000000000000000000000000000000000..f7676d0182d5414f820694dccdf67937e849d64c GIT binary patch literal 840 zcmX9-eMnPL6yKPxlD&isbx)H`G0O_?_pkrh1QRhuGW2{EIklqrej{>jdrT;9E$!+Gca&hPiT?Zw)BB8&_Z z2n2+>Ag2^gGkmh$V%R^$8xsWrQMfuM>x@abI#p7dP`RI)O0Z09hyI|xnvO57I3=#B zhYiNd9j%+$`z+s`RYpf=Rl(A_GNh;T`LZ*rJ40!Sm|PCE48=cMEEAy^f;&oNBE?Vc z^{|j_N|W6v7oqTZGjBLcH2&>dw?Ab7EvPfB~Ju@DP-fgAnqHV zz_F*-diF?Xv`v z)zcp1mII(XF#kS>`RwE^WNSg zF}hmoI>JtB-PY!hY(7SU$@h-!j6KG+Jcigf&d4_sK_{C-XIn5tV>f`Bz07@kAWRC< z5Ey_4_l(5SVO%vqaj@)6Us^pUOC`Ywg(B3+8(eDl$cNZ}v&IS(K4<3d@gv%nToD4` ztJAVJ{yq6-JoN=w%Jx$2E=P(Bb18J58+<|esU0g}Cx9w&K<*;o;yhz;k9l^ORP);f&aCrn2GmjTLImnuZO&-+r91xt8c<#% literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer/00007.png b/tests/snapshots/nanos_erc721_transfer/00007.png new file mode 100644 index 0000000000000000000000000000000000000000..9eeff67dddcada1d76dd22bea2465359806d2435 GIT binary patch literal 888 zcmYk5ZAcSw9LF~<4%7WL(#$&zraer`C(c=ll$)o{5l$(Erlk%JnGd99nbKa2kQ<`X zsimBcn2HaP2_s0dM6T#wEK!q4vut~DL8$oY$+}D!rB(3@cGtz;`Uaf&ddhjysr4?h>ZJbgc#I0j|j$EHx$37m(HL7jn{?~)J5BeFXliV^O9kF;R4lDUyoaw-s+JoUzoizU6 zvvjv->XFMN0Bc(l4h+iS1rG4>GieuIV9R82=5-cE6@83jf(dX0VzQDK3Any27)<*{ zxo5dTSaF`TzJT&#AtH_QEjYssxC@}P4*(KF{ne5a8Z`^u=xG@3T7+Lo Op)jVK(k4=i@BahK?Q|Rf literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer/00008.png b/tests/snapshots/nanos_erc721_transfer/00008.png new file mode 100644 index 0000000000000000000000000000000000000000..c830a35aecb039d4f815a86af2a9acd8282243a2 GIT binary patch literal 629 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfiG8x;TbZFupx_J5Sl2!TI3f z#vlLX&#=3_h&8pD^yY@NX7|pJ&ihrWotHPR2#BXRd5e0_iYWbHZjq|9t znt6EhFFG7u8ccOP(Z#a{go|3UceZd_Qh2mQq*;4Yv@}wqMzMaFvYtC(MWHP60 z9plW|t^Fw(7H>nCjpR8szDrHG*#q={mPE=jU?|3$@&J9Wuvrplo^ZgnHHdxWI keOQvgz1%t4LiUz@(B*%fu{C=Z0+SPir>mdKI;Vst06OylmH+?% literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer/00009.png b/tests/snapshots/nanos_erc721_transfer/00009.png new file mode 100644 index 0000000000000000000000000000000000000000..11c298f502d78adcf62a35b9cab2e6523a1f34de GIT binary patch literal 511 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfbnTba4!+V0?QpFz>blgY!Xm z$36cWe;(xOj_SzSo^k7n=aRXC_NB_5FQcyTO6TsHc%(#@>C`gaTO~jtDY1Jm+Fk~+ zObO4HNa2%Wn^}MDgTu4KrV5*Zd^r;Z=fi$!*Bj1gubngVjJGL}+su~2HZzvlXnyKb z?QDw)l2Ra4IlXP}nRSWk#wKr4>b#%rj{KoxWb#I8g5?B9wwe5w8+eS^c~2;Oc3_(c zr2kB1lbUezzNA^)r%FJ znSmxYoNTrZa7m? zWMpzj=gx@>26JPWjm)|8PXbv$SLK5Z`T0$XFQu)G-3Vv{&?hz7yeIBNSGqkjfw(ND z>5MV=<)v%q#2)s0)^8GX^wiChdu9XO5vl7leVak-ty4Em-=r*4W=!3+ruF3|v1Q)T jGxTy3cWnW3>KM-WtzW%&o?Ip{Vi`PL{an^LB{Ts5TJzP~ literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer/00010.png b/tests/snapshots/nanos_erc721_transfer/00010.png new file mode 100644 index 0000000000000000000000000000000000000000..0a287c791068a3e3598b4c2155e958006f235d91 GIT binary patch literal 796 zcmX|9e@GKy7~Wi5BD)0{>Mq319~faN-)=*~aR# zGUZxUIs`GX#eyhSA~)z(7tL9g5J9r{Mg`y9clUnJ_q^}>Ja5MtOA($x zBuFF@+G*?9bHt8``Zduc}1KEq^I`>&m}vEAJZ0k6k} zJ`7J{R(AfzYbFqRHj->z8cM#CU!agK4jp>6bSqPFI}%8{HW?X=Kk#D4%h!=PXlKk; z6rI+pe59Nmz$ixw@<`cpM>gESxn9Acx05O#--S`Di$cqcSM7seQnhgyH`oC`Y~#0e z*{dd+o^lGb9Pw@UF^ZXW=rJ}`Ni`G97K0It5|MLZc5N(jP)Q`Q$=W(T2d~gtKRX4t zR;3}BS*D6$UiND?GYwpN(>)=)UXLn*)+}F!m}(TGu6HIDmdFqvYDh%ncDW1{2HCao zNLetHM@UlL-j^TV;Cq}(7%FxGw)^RDzZ8?S&_5z&yJ?f1tb=ot-6O(fD7RKGekyUkpe0JMPYAKs*&4k)wrDyvrukO} z0CF~!1sTJ!W#KF`6r=hdf&&Xztz9Nn296fA+4ZLAd1`f33fJ5Yebj{W%e?rdg6E0t?alM+q2IDbTUluPLb)HNUSc*` KjN>OR-uwgM6JvY; literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer/00011.png b/tests/snapshots/nanos_erc721_transfer/00011.png new file mode 100644 index 0000000000000000000000000000000000000000..8867437398c01a71e89db6514d605a9344eeba88 GIT binary patch literal 492 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfeZRba4!+V0?QpFi+W?!TI3v z#)|*UpPQ{STzI|1nqrSkocnT}Q2k3!q1#bcgw^L(O**oTkMZdhF)*vGnZGJ2x~t(+ z9&huR<~GM?@!3CIo}D&TFgEcqns1`ud|1WURN=Fq`8uCxYg3l7fe_owm5ejr7d=+b zt^g_nq0jD8YBN_RPLc#tt9O2kP04Ub%W!CG^GwV7A;f!vhc}qn$oVky%;{1SDlZtE z__MgpF>TU9#+l_iKTbB?qvCv6;q!JWIiNay(>+%j&g4&&l$roUm0+`D^J>&3rPyXp zPibqjSNlBajJMHzpwbgDXAk}lWHvf+;e(iwNzB>a`Rg8fpS)mjlE(<>bfCLGJG8L@ zwRi$u^2`zH4W?6@*;2OMztC`II#AU9V#Aru&FQ=+a>SLL4;wQ+UB@`{?IEcNJFRzQ zJEZNlkd%7I_bw&lh8|FScXL08wTf|OKg$%LmY67@f4s9Dp3Q97w2LhztYm}k+k{fy z6Kh^2MBh4iN@_yn&4b&vH0NeAo!YhrNU`6(#J6p!Y>v0}jcMPwB`0nu?qiNx35-Ps MPgg&ebxsLQ06PWH3IG5A literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer/00012.png b/tests/snapshots/nanos_erc721_transfer/00012.png new file mode 100644 index 0000000000000000000000000000000000000000..3158ea690450f2b8c85c12004b002b02e7b02539 GIT binary patch literal 582 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfj3Yx;TbZFupw)IO&lagUi8i zu2=uq*Yz9Se)!g*VK4JBV-C&WKWoz`S-k14IyUoMm0IWFIu4EW+>4RtRSIvkw{qJ) z-k{BNYMKd;QTzYAL~p5OZSof#4llR(=qVMJ($*$Ab77)uTE-4V=fhVFPUOsN*u=_P zev*f``Ha_Lh0T`?P5_w@bmoG=iJGR?wG$oFmOfn9wp(f$o7BG*4~6I6nAJ9Y(#_4S zx*0Q4Cd`zYu+#gQ-puxsHA;+6txb81;;$K;SOc`)`Eb{n3mcS-P1XR-G*4O9=9zYy zWlA>hi9IogRX%T%Vw>52vEj_tR;Wnvk2E8bnCKl>8qOS+%J|{VJNeAR_7fKhJRN>6 zWt_R(#K%Y*B*bf?@cHzid?OPZ*R-q|Qfe~~OJ_JdGiQ7n#BAiBxx@R}2R5LKwiNib zwJkq!XF{bkuSv~rHmSN0W~1w3?WueMPz z$jMOtJF#s}Y?8nbo644w2lP(7Qu7&Lh$$GG`0$>%lOd6E?`G%PjEacEho#sgfuXFQ zeWOb_8W?6gy!z_Ez}=wqxl_z;<{4*T7^Tcp+APW)4b*$#gTTy%593WQI2=|m1}X&# zr~*UES~!4j_tt_NTU!sW)0HX9eYhc;yFK@KDXZ;sPq04($@Y}pt$UkYuJ7O_vMLEmi+4c;Fw95@=>aR7Nxv47?$ZTZtr_3n+(gx*XR_$~kWo+{1#Gb1SXD%3= zxUfOl`S7wtWoKq1=fesh37M0pZl2)bJ;7rH6tf4a>N|5`!^DM*Gq+1kkOZQXHnx<$ zq^B8D6K1Bx%Ze~iP)OohG zIi_9y5O&xt%^k>mro%ENocDx`AG1-use^*|uq^-bNuC4jCC(rnYh>Cs(MmB?|tDnm{ Hr-UW|v$x^l literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer_with_eth/00000.png b/tests/snapshots/nanos_erc721_transfer_with_eth/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..2994983f84cf76ab587bac356d5afe297c0e0632 GIT binary patch literal 541 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfcyxba4!+V0?SfW}LV) zm52AlpJ_b2>@)W(0ade0N=>L#7k{_%p*K+Izf)VA1-(C=WRZ-BymEd z+ycg#=gr>vKI=O3DZ$93W?LaYQ1QY>pxO%?Y&v*&<#TnOD>)zbXF6qTB9pT3N&!&M z3dWi9&EBnj=ymuLi`0al9u7OFx80vqnaz8`W+_{Wop8XrwTVif&jYR3;ytm)i1F!7 zQ=gO!i4>qCfqpe|KCDm-Wb*LtR{=T`$TJ28kKEHk+7p0Y?mPqLwy~wK0ofM}OcabW z9iDmf0+pZOF#@XZFL)Sha%TzS%!Lnq%!RyR`IX+l PXlL+r^>bP0l+XkKp5EPW literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer_with_eth/00001.png b/tests/snapshots/nanos_erc721_transfer_with_eth/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..6ff345b740b4a214d00be4aa68764e985e20cd02 GIT binary patch literal 408 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfhh@x;TbZFupx_vG0n3h|9st zVZZ*{|FHYwCYbCiG%a?5!;!M7KNPJ!b&3Pa-fTJTCv@6|r6pDf%#xAH-;_MFtHEHE z$cZ^i9G*>hK*O$na(uRZ`ov^1JyMr zST3=cSUIUTaOn?a%?Uq)x&(82e1P7IPT>P`Rc8vCOtY2@I+Ku`qFLwN<@YSuXvvF| ld0q|?%dSQ&yM~0mF_ySC=ANCH)&UH022WQ%mvv4FO#rmFqe%b& literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer_with_eth/00002.png b/tests/snapshots/nanos_erc721_transfer_with_eth/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..36ec626b410aa243eadc783ac3fd5ffb3c2a58dd GIT binary patch literal 688 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFff&Sx;TbZFupw)nD;1(!R6p( zvAzFUzq|AAs){)}!^Y!~26NfeAO7c63U7#i?J=JF)kmoPuRy@LTZ=Z&^L(^{pIiP* z&*60(8tYdiIzRK4nlMv}ZDuc9O2&+oWo#*NXFJ1HjZG9jC-a`Tu;F6E83kigh0mw_ z_N;F6e5Ng| zrDQlf3+6qs#>B@cnAvE(o5Rj)4QFm=J3P~rn(#B-RG~QcVHC4byScLSVN=GZix_7f zmIg{DJp{34PhR*i>7i3w+u_+@F=a_9zWh~;Gy7SlWb>Z5bH(7qovRxZKd)p|) zmQv@+Y~&A&C|{t0#Vk|S15E~63yhYwHr|>3z-YN-aPooy&=hT;g03^bQ1byg;)=n^ zHDLE>8#y0VF*aQT6xB8|QP>PL$(HF<;&w5<-P;OoY;8TfPFJQZ7Z|Da3<0qUWyjUt R3IkIjgQu&X%Q~loCIHci8(9DV literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer_with_eth/00003.png b/tests/snapshots/nanos_erc721_transfer_with_eth/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..b656cdff301084d3d91fc5796c44c5723913bfa8 GIT binary patch literal 698 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfcWGx;TbZFupw)SR@@T;(V}K zGwDC~G4?}Mo$G9+ZcS0TojU2V?!U|0o^#^5tBy`xSEbr{`L#g6x?77@$9X>bB4xJk zvbLYTt3#y=FT2tFOATi(8Km$@O<0(CqK7R-?rf{O>Shy#X>8E0N>IHP1NHK8&^ zd|v3`36^)u9!i=j00nMlIy|e-j7VwY;XPq9srPamGf1_pWJ*RwiS+V(pbk~(<@Kft zpR@FAlqIFw&NKoQ_OvNFGaFg+I;R1R&tRGI9jL5|_rxC|Al?dO=Re-dmg48{*4Fl& zWr{iPi8=Ekn2qwy?wKeQR}~qV?73N??0op;Nua4|kE@ML=FAIWHo7kya4*{-EmHz$ zZuglCiwQp$wz;O6$eg%fkm7cDf+Wy~CT~vMxzcbZ!{OPoHs+ZZ3luh+=s;YdV`LAJ z*vq~B1P{owpulK2GvVh1wiKXQmO%X{bU;2+ES`ME-?V1d89!zt=VuETXD$V!X10`R zY{0PNOUb+WW3p7ng2ahGwM`X@gPD0v{!Epc@N)&X{KX9?cqFC7X6^=ZHv!$Y9H?eO zn<6j}e=35k%)e}q@-EBanXz6?3)Fs~6Ebd0{JEt~cBX4urbF5Z9Z=u_L)Y!F{N)YG zn@#ShY&Lmg@<&U&j(w*7GM(C>74Bv3&a0!m3Q^ f<Y0w#W<@%`Lny!1W75indZDFE*PBj;XN_u?77I3JiI6Vq#Fe@gHZFCD-CC6zKsbz ztg^XgTd^ze37{$&6NTan1}FXm9abyS&qw{})V_Vxw#+l5LDH#^Kd!Jr2n-+b0 zrxMt3BNGK<6CWdIW+Qgq$sqG?a%ilVny_;ZTgpFipj{EXC-&T`xY}^0$k51Sk5Iro zsR@yKYZzxP?_JK8GOc^^!iSlUJ=@xzK9ZVHdFzK4khMr^!cQ-UN}!hh%LNLb13@M? zpK$~_$5aOBKrr*OvZ=!7tmncr<-MOxm%0aX0}KI`0KEou4+Powol%z#ztnK%)9q|BH)I`}0Nf3KmVC=ke%+4a=0t+g>bDpJZXv-WpzY z=;2BZjq0o$6MhCT8#yx@*PJ?v9MUpwoIbTT^G7hq(8UjrOMTl` zl%Mjh?aYM_x<=K}H=++8p1km(+}byuZER9%GacL7JfArmnfMqrgRHw?a3bf#1%s1+ zX7KPz^WIl;KCH5to$+Z5vr#+yrG_&*TZ?&5$jJ5Z@IDud%XWD7VL?hpMeajakhHq< z;qF#(BNG`pplW@wIG`;b7D!E~^mVYj4hoB$6E)tePoFpk_2K1ykIG})D1o?8pOrY~^ z*ZxW6J>dfiFL1B}NqKM>0A(l4Olf0F+2;ccqO?gnTc<{6&Pb_q1$yY>hvmSS**-OT z;?7-dj?aQq?x}uG3FY%}kg=A5{r%=q-f j7BN2m+=m~o{9`OD<=Mer>@gXb02w@8{an^LB{Ts5=zt|t literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer_with_eth/00006.png b/tests/snapshots/nanos_erc721_transfer_with_eth/00006.png new file mode 100644 index 0000000000000000000000000000000000000000..f7676d0182d5414f820694dccdf67937e849d64c GIT binary patch literal 840 zcmX9-eMnPL6yKPxlD&isbx)H`G0O_?_pkrh1QRhuGW2{EIklqrej{>jdrT;9E$!+Gca&hPiT?Zw)BB8&_Z z2n2+>Ag2^gGkmh$V%R^$8xsWrQMfuM>x@abI#p7dP`RI)O0Z09hyI|xnvO57I3=#B zhYiNd9j%+$`z+s`RYpf=Rl(A_GNh;T`LZ*rJ40!Sm|PCE48=cMEEAy^f;&oNBE?Vc z^{|j_N|W6v7oqTZGjBLcH2&>dw?Ab7EvPfB~Ju@DP-fgAnqHV zz_F*-diF?Xv`v z)zcp1mII(XF#kS>`RwE^WNSg zF}hmoI>JtB-PY!hY(7SU$@h-!j6KG+Jcigf&d4_sK_{C-XIn5tV>f`Bz07@kAWRC< z5Ey_4_l(5SVO%vqaj@)6Us^pUOC`Ywg(B3+8(eDl$cNZ}v&IS(K4<3d@gv%nToD4` ztJAVJ{yq6-JoN=w%Jx$2E=P(Bb18J58+<|esU0g}Cx9w&K<*;o;yhz;k9l^ORP);f&aCrn2GmjTLImnuZO&-+r91xt8c<#% literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer_with_eth/00007.png b/tests/snapshots/nanos_erc721_transfer_with_eth/00007.png new file mode 100644 index 0000000000000000000000000000000000000000..9eeff67dddcada1d76dd22bea2465359806d2435 GIT binary patch literal 888 zcmYk5ZAcSw9LF~<4%7WL(#$&zraer`C(c=ll$)o{5l$(Erlk%JnGd99nbKa2kQ<`X zsimBcn2HaP2_s0dM6T#wEK!q4vut~DL8$oY$+}D!rB(3@cGtz;`Uaf&ddhjysr4?h>ZJbgc#I0j|j$EHx$37m(HL7jn{?~)J5BeFXliV^O9kF;R4lDUyoaw-s+JoUzoizU6 zvvjv->XFMN0Bc(l4h+iS1rG4>GieuIV9R82=5-cE6@83jf(dX0VzQDK3Any27)<*{ zxo5dTSaF`TzJT&#AtH_QEjYssxC@}P4*(KF{ne5a8Z`^u=xG@3T7+Lo Op)jVK(k4=i@BahK?Q|Rf literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer_with_eth/00008.png b/tests/snapshots/nanos_erc721_transfer_with_eth/00008.png new file mode 100644 index 0000000000000000000000000000000000000000..c830a35aecb039d4f815a86af2a9acd8282243a2 GIT binary patch literal 629 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfiG8x;TbZFupx_J5Sl2!TI3f z#vlLX&#=3_h&8pD^yY@NX7|pJ&ihrWotHPR2#BXRd5e0_iYWbHZjq|9t znt6EhFFG7u8ccOP(Z#a{go|3UceZd_Qh2mQq*;4Yv@}wqMzMaFvYtC(MWHP60 z9plW|t^Fw(7H>nCjpR8szDrHG*#q={mPE=jU?|3$@&J9Wuvrplo^ZgnHHdxWI keOQvgz1%t4LiUz@(B*%fu{C=Z0+SPir>mdKI;Vst06OylmH+?% literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer_with_eth/00009.png b/tests/snapshots/nanos_erc721_transfer_with_eth/00009.png new file mode 100644 index 0000000000000000000000000000000000000000..84d25eba887a7be381cbce094ab78906813023d3 GIT binary patch literal 404 zcmV;F0c-w=P)TZe$KNrE=nNTRJ_ z@F5%8QUru2OM%Qu3S@REZe&PtBXg4inO%w-nJ0TOgA~a8k<5+EE(J0_CUcSknWq}e zK!z0U-woQ*Ud%#<6v*79IFV`JUaUZ-{oY_EGNfof_hL3Or1)=AoX9+xfN+%pnKqFW z$XpGsQXtbNBSVU-!KW0+v`NX3;?v+Q1u|`NGNgDLyvdNFO)dp8Z-Y-Vq-c{$fy^fX z;VK0(kCoyebCO~wb4YO_vrBQ1d9)PBTnPv>OM%QT1v0x7$lRnr<|YL)vl+Cd2?#Sv zv6DGTv6Hz;fy_yYoy?2|vywrE6o(YZAVZ2DkwJzOGa9sSDUeyspe^mij8YtA4l<blgY!Xm z$36cWe;(xOj_SzSo^k7n=aRXC_NB_5FQcyTO6TsHc%(#@>C`gaTO~jtDY1Jm+Fk~+ zObO4HNa2%Wn^}MDgTu4KrV5*Zd^r;Z=fi$!*Bj1gubngVjJGL}+su~2HZzvlXnyKb z?QDw)l2Ra4IlXP}nRSWk#wKr4>b#%rj{KoxWb#I8g5?B9wwe5w8+eS^c~2;Oc3_(c zr2kB1lbUezzNA^)r%FJ znSmxYoNTrZa7m? zWMpzj=gx@>26JPWjm)|8PXbv$SLK5Z`T0$XFQu)G-3Vv{&?hz7yeIBNSGqkjfw(ND z>5MV=<)v%q#2)s0)^8GX^wiChdu9XO5vl7leVak-ty4Em-=r*4W=!3+ruF3|v1Q)T jGxTy3cWnW3>KM-WtzW%&o?Ip{Vi`PL{an^LB{Ts5TJzP~ literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer_with_eth/00011.png b/tests/snapshots/nanos_erc721_transfer_with_eth/00011.png new file mode 100644 index 0000000000000000000000000000000000000000..0a287c791068a3e3598b4c2155e958006f235d91 GIT binary patch literal 796 zcmX|9e@GKy7~Wi5BD)0{>Mq319~faN-)=*~aR# zGUZxUIs`GX#eyhSA~)z(7tL9g5J9r{Mg`y9clUnJ_q^}>Ja5MtOA($x zBuFF@+G*?9bHt8``Zduc}1KEq^I`>&m}vEAJZ0k6k} zJ`7J{R(AfzYbFqRHj->z8cM#CU!agK4jp>6bSqPFI}%8{HW?X=Kk#D4%h!=PXlKk; z6rI+pe59Nmz$ixw@<`cpM>gESxn9Acx05O#--S`Di$cqcSM7seQnhgyH`oC`Y~#0e z*{dd+o^lGb9Pw@UF^ZXW=rJ}`Ni`G97K0It5|MLZc5N(jP)Q`Q$=W(T2d~gtKRX4t zR;3}BS*D6$UiND?GYwpN(>)=)UXLn*)+}F!m}(TGu6HIDmdFqvYDh%ncDW1{2HCao zNLetHM@UlL-j^TV;Cq}(7%FxGw)^RDzZ8?S&_5z&yJ?f1tb=ot-6O(fD7RKGekyUkpe0JMPYAKs*&4k)wrDyvrukO} z0CF~!1sTJ!W#KF`6r=hdf&&Xztz9Nn296fA+4ZLAd1`f33fJ5Yebj{W%e?rdg6E0t?alM+q2IDbTUluPLb)HNUSc*` KjN>OR-uwgM6JvY; literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer_with_eth/00012.png b/tests/snapshots/nanos_erc721_transfer_with_eth/00012.png new file mode 100644 index 0000000000000000000000000000000000000000..8867437398c01a71e89db6514d605a9344eeba88 GIT binary patch literal 492 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfeZRba4!+V0?QpFi+W?!TI3v z#)|*UpPQ{STzI|1nqrSkocnT}Q2k3!q1#bcgw^L(O**oTkMZdhF)*vGnZGJ2x~t(+ z9&huR<~GM?@!3CIo}D&TFgEcqns1`ud|1WURN=Fq`8uCxYg3l7fe_owm5ejr7d=+b zt^g_nq0jD8YBN_RPLc#tt9O2kP04Ub%W!CG^GwV7A;f!vhc}qn$oVky%;{1SDlZtE z__MgpF>TU9#+l_iKTbB?qvCv6;q!JWIiNay(>+%j&g4&&l$roUm0+`D^J>&3rPyXp zPibqjSNlBajJMHzpwbgDXAk}lWHvf+;e(iwNzB>a`Rg8fpS)mjlE(<>bfCLGJG8L@ zwRi$u^2`zH4W?6@*;2OMztC`II#AU9V#Aru&FQ=+a>SLL4;wQ+UB@`{?IEcNJFRzQ zJEZNlkd%7I_bw&lh8|FScXL08wTf|OKg$%LmY67@f4s9Dp3Q97w2LhztYm}k+k{fy z6Kh^2MBh4iN@_yn&4b&vH0NeAo!YhrNU`6(#J6p!Y>v0}jcMPwB`0nu?qiNx35-Ps MPgg&ebxsLQ06PWH3IG5A literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_erc721_transfer_with_eth/00013.png b/tests/snapshots/nanos_erc721_transfer_with_eth/00013.png new file mode 100644 index 0000000000000000000000000000000000000000..3158ea690450f2b8c85c12004b002b02e7b02539 GIT binary patch literal 582 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfj3Yx;TbZFupw)IO&lagUi8i zu2=uq*Yz9Se)!g*VK4JBV-C&WKWoz`S-k14IyUoMm0IWFIu4EW+>4RtRSIvkw{qJ) z-k{BNYMKd;QTzYAL~p5OZSof#4llR(=qVMJ($*$Ab77)uTE-4V=fhVFPUOsN*u=_P zev*f``Ha_Lh0T`?P5_w@bmoG=iJGR?wG$oFmOfn9wp(f$o7BG*4~6I6nAJ9Y(#_4S zx*0Q4Cd`zYu+#gQ-puxsHA;+6txb81;;$K;SOc`)`Eb{n3mcS-P1XR-G*4O9=9zYy zWlA>hi9IogRX%T%Vw>52vEj_tR;Wnvk2E8bnCKl>8qOS+%J|{VJNeAR_7fKhJRN>6 zWt_R(#K%Y*B*bf?@cHzid?OPZ*R-q|Qfe~~OJ_JdGiQ7n#BAiBxx@R}2R5LKwiNib zwJkq!XF{bkuSv~rHmSN0W~1w3?WueMPz z$jMOtJF#s}Y?8nbo644w2lP(7Qu7&Lh$$GG`0$>%lOd6E?`G%PjEacEho#sgfuXFQ zeWOb_8W?6gy!z_Ez}=wqxl_z;<{4*T7^Tcp+APW)4b*$#gTTy%593WQI2=|m1}X&# zr~*UES~!4j_tt_NTU!sW)0HX9eYhc;yFK@KDXZ;sPq04($@Y}pt$UkYuJ7O_vMLEmi+4c;Fw95@=>aR7Nxv47?$ZTZtr_3n+(gx*XR_$~kWo+{1#Gb1SXD%3= zxUfOl`S7wtWoKq1=fesh37M0pZl2)bJ;7rH6tf4a>N|5`!^DM*Gq+1kkOZQXHnx<$ zq^B8D6K1Bx%Ze~iP)OohG zIi_9y5O&xt%^k>mro%ENocDx`AG1-use^*|uq^-bNuC4jCC(rnYh>Cs(MmB?|tDnm{ Hr-UW|v$x^l literal 0 HcmV?d00001 diff --git a/tests/src/erc1155.test .js b/tests/src/erc1155.test .js new file mode 100644 index 0000000..59eeee0 --- /dev/null +++ b/tests/src/erc1155.test .js @@ -0,0 +1,27 @@ +import "core-js/stable"; +import "regenerator-runtime/runtime"; +import { waitForAppScreen, zemu, txFromEtherscan } from './test.fixture'; +import { TransportStatusError } from "@ledgerhq/errors"; + +// ------------------- +// TODO: Actually write the tests + +test.skip('[Nano S] Transfer 1155', zemu("nanos", async (sim, eth) => { + + const rawTx = "" + const serializedTx = txFromEtherscan(rawTx); + + // with ETH need to test + // const serializedTx = txFromEtherscan("0x02f901350182022f8459682f0085246ad7eb3182de2994424db67b40b15ed85475c3f29dedf601b6ee75b283424242b8c4f242432a000000000000000000000000dcdb88f3754b2841093d9348a2d02df8cf06314c000000000000000000000000df9fb2eff1f2871caeeb94bf262ffba84efddddc0000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000c001a0c4283f86dc852e43e9fd1077b448c63fec76bdeb44dfac977730725e41fa3676a0543b2d2f99f65fb20cd548964eee94b1c1865919f4574c7089d8b95678b667c2"); + + const tx = eth.signTransaction( + "44'/60'/1'/0/0", + serializedTx, + ); + + await waitForAppScreen(sim); + await sim.navigateAndCompareSnapshots('.', 'nanos_erc721_transfer_ethereum', [12, 0]); + + await expect(tx).resolves.toEqual({ + }); +})); diff --git a/tests/src/erc721.test.js b/tests/src/erc721.test.js new file mode 100644 index 0000000..111d501 --- /dev/null +++ b/tests/src/erc721.test.js @@ -0,0 +1,130 @@ +import "core-js/stable"; +import "regenerator-runtime/runtime"; +import { waitForAppScreen, zemu, txFromEtherscan } from './test.fixture'; + +test('[Nano S] Transfer erc721', zemu("nanos", async (sim, eth) => { + + // https://etherscan.io/tx/0x73cec4fc07de3a24ba42e8756e13b7ddfa9bd449126c37640881195e8ea9e679 + // Modified to put a bigger token id + const rawTx = "0x02f8d101058459682f0085233da9943e8301865b94bd3531da5cf5857e7cfaa92426877b022e612cf880b86423b872dd0000000000000000000000004cc568b73c0dcf8e90db26d7fd3a6cfadca108a3000000000000000000000000d4c9b20950c3eca38fc1f33f54bdf9694e488799ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc080a094c8632fe7277aa8c54cea9d81a15911cfa4970a2bf7356d14d04cc5afbcdab7a013a77b8c79e5d9b2b35edb3c44db3bb41b92f5c463ff126bf19d213b2b9ba8b5" + const serializedTx = txFromEtherscan(rawTx); + + const tx = eth.signTransaction( + "44'/60'/1'/0/0", + serializedTx, + ); + + await waitForAppScreen(sim); + await sim.navigateAndCompareSnapshots('.', 'nanos_erc721_transfer', [12, 0]); + + await expect(tx).resolves.toEqual({ + "r": "59f6a9769cff66eed8be8716c44d39808d1e43f3aa0bb97538e124dba4bc4565", + "s": "662990a841c663a165ba9a83e5cc95c03a999b851e0bd6d296aa70a0f7c96c1a", + "v": "01", + }); +})); + +test('[Nano S] Transfer erc721 with attached ETH', zemu("nanos", async (sim, eth) => { + + const rawTx = "0x02f8d601058459682f0085233da9943e8301865b94bd3531da5cf5857e7cfaa92426877b022e612cf8854242424242b86423b872dd0000000000000000000000004cc568b73c0dcf8e90db26d7fd3a6cfadca108a3000000000000000000000000d4c9b20950c3eca38fc1f33f54bdf9694e4887990000000000000000000000000000000000000000000000000000000000000f21c080a094c8632fe7277aa8c54cea9d81a15911cfa4970a2bf7356d14d04cc5afbcdab7a013a77b8c79e5d9b2b35edb3c44db3bb41b92f5c463ff126bf19d213b2b9ba8b5" + const serializedTx = txFromEtherscan(rawTx); + + + const tx = eth.signTransaction( + "44'/60'/1'/0/0", + serializedTx, + ); + + await waitForAppScreen(sim); + await sim.navigateAndCompareSnapshots('.', 'nanos_erc721_transfer_with_eth', [13, 0]); + + await expect(tx).resolves.toEqual({ + "r": "9c42e10b49f3ee315ab2d5f7ad96f1068c75578734b66504716cc279ead27d47", + "s": "45dde78470ad75ffdb27a799b87e4934e2e10e98dbc6f88bc4a9bc19c4de86bc", + "v": "00", + }); +})); + +test('[Nano S] set approval for all erc721', zemu("nanos", async (sim, eth) => { + + // https://etherscan.io/tx/0x86b936db53c19fddf26b8d145f165e1c7fdff3c0f8b14b7758a38f0400cfd93f + const rawTx = "0x02f8b0010c8459682f00852cfbb00ee682b54294d4e4078ca3495de5b1d4db434bebc5a98619778280b844a22cb4650000000000000000000000002efcb1e8d4472d35356b9747bea8a051eac2e3f50000000000000000000000000000000000000000000000000000000000000001c001a0c5b8c024c15ca1452ce8a13eacfcdc25f1c6f581bb3ce570e82f08f1b792b3aca03be4dba0302ae190618a72eb1202ce3af3e17afd7d8a94345a48cae5cad15541"; + const serializedTx = txFromEtherscan(rawTx); + + + const tx = eth.signTransaction( + "44'/60'/1'/0/0", + serializedTx, + ); + + await waitForAppScreen(sim); + await sim.navigateAndCompareSnapshots('.', 'nanos_erc721_approval_for_all', [12, 0]); + + await expect(tx).resolves.toEqual({ + "r": "8b6a70a1fe76d8e9b1250531a17eb1e367936732d4dfb9befc81a5031b271dc8", + "s": "7658d7151bba0d8504cea2013bead64cb8407dc6be1fca829bb9594b56f679af", + "v": "00", + }); +})); + +// NOT DONE +test.skip('[Nano S] approval erc721', zemu("nanos", async (sim, eth) => { + + // INCORRECT, need to find / create an approval tx + const rawTx = ""; + const serializedTx = txFromEtherscan(rawTx); + + + const tx = eth.signTransaction( + "44'/60'/1'/0/0", + serializedTx, + ); + + await waitForAppScreen(sim); + await sim.navigateAndCompareSnapshots('.', 'nanos_erc721_approval', [12, 0]); + + await expect(tx).resolves.toEqual({ + }); +})); + +test('[Nano S] safe transfer erc721', zemu("nanos", async (sim, eth) => { + + // https://etherscan.io/tx/0x1ee6ce9be1c9fe6f030ff124ba8c88a410223c022816547e4b3fedd3a4d2dc1e + const rawTx = "0xf8cc82028585077359400083061a8094d4e4078ca3495de5b1d4db434bebc5a98619778280b86442842e0e000000000000000000000000c352b534e8b987e036a93539fd6897f53488e56a0000000000000000000000000a9287d9339c175cd3ea0ad4228f734a9f75ee6200000000000000000000000000000000000000000000000000000000000000621ca08250f4b2c8f28c5e4ef621dba4682990d1faf930c8cb6d032c6e7278e8951d92a03c1e1f6d63ed339041f69f24c6c0968ba26f244f779cb4fa7a468f3ba3d3e06e"; + const serializedTx = txFromEtherscan(rawTx); + + + const tx = eth.signTransaction( + "44'/60'/1'/0/0", + serializedTx, + ); + + await waitForAppScreen(sim); + await sim.navigateAndCompareSnapshots('.', 'nanos_erc721_safe_transfer', [10, 0]); + + await expect(tx).resolves.toEqual({ + "r": "b936684d5d0e99e09701021fb73ae9403f2ec79414d822d42c5bd1c0a2118f1a", + "s": "23e517c6cac998f392d179be2fe7c3225f0e0a165b1af85548da5d6acaa73c4f", + "v": "25", + }); +})); + +// NOT DONE +test.skip('[Nano S] safe transfer with data erc721', zemu("nanos", async (sim, eth) => { + + // need to find or create a safe transfer with data on etherscan? + const rawTx = ""; + const serializedTx = txFromEtherscan(rawTx); + + + const tx = eth.signTransaction( + "44'/60'/1'/0/0", + serializedTx, + ); + + await waitForAppScreen(sim); + await sim.navigateAndCompareSnapshots('.', 'nanos_erc721_safe_transfer_with_data', [12, 0]); + + await expect(tx).resolves.toEqual({ + }); +})); \ No newline at end of file diff --git a/tests/src/test.fixture.js b/tests/src/test.fixture.js index fcad8f7..70b8313 100644 --- a/tests/src/test.fixture.js +++ b/tests/src/test.fixture.js @@ -1,5 +1,6 @@ import Zemu from '@zondax/zemu'; import Eth from '@ledgerhq/hw-app-eth'; +import {RLP} from "ethers/lib/utils"; const transactionUploadDelay = 60000; @@ -36,6 +37,35 @@ const NANOX_CLONE_ELF_PATH = Resolve("elfs/ethereum_classic_nanox.elf"); const TIMEOUT = 1000000; +// Generates a serializedTransaction from a rawHexTransaction copy pasted from etherscan. +function txFromEtherscan(rawTx) { + // Remove 0x prefix + rawTx = rawTx.slice(2); + + let txType = rawTx.slice(0, 2); + if (txType == "02" || txType == "01") { + // Remove "02" prefix + rawTx = rawTx.slice(2); + } else { + txType = ""; + } + + let decoded = RLP.decode("0x" + rawTx); + if (txType != "") { + decoded = decoded.slice(0, decoded.length - 3); // remove v, r, s + } else { + decoded[decoded.length - 1] = "0x"; // empty + decoded[decoded.length - 2] = "0x"; // empty + decoded[decoded.length - 3] = "0x01"; // chainID 1 + } + + // Encode back the data, drop the '0x' prefix + let encoded = RLP.encode(decoded).slice(2); + + // Don't forget to prepend the txtype + return txType + encoded; +} + function zemu(device, func) { return async () => { jest.setTimeout(TIMEOUT); @@ -71,5 +101,6 @@ module.exports = { NANOX_CLONE_ELF_PATH, sim_options_nanos, sim_options_nanox, - TIMEOUT + TIMEOUT, + txFromEtherscan, } \ No newline at end of file From c6864d1d42e2aa4f574eb416ad4b10b864db7d0f Mon Sep 17 00:00:00 2001 From: apaillier-ledger <94451027+apaillier-ledger@users.noreply.github.com> Date: Mon, 22 Nov 2021 14:58:50 +0100 Subject: [PATCH 3/9] Bump app version to 1.9.14 (#219) * Bump app version * Updated the changelog file --- CHANGELOG.md | 13 ++++++++++++- Makefile | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e429752..d008b39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,13 +5,24 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [1.9.14](https://github.com/ledgerhq/app-ethereum/compare/1.9.13...1.9.14) - 2021-11-22 + +### Added + +- Support for Non-Fungible Token (ERC 721 & ERC 1155) + +## [1.9.13](https://github.com/ledgerhq/app-ethereum/compare/1.9.12...1.9.13) - 2021-11-17 + +### Changed + +- Small improvement in app size + ## [1.9.12](https://github.com/ledgerhq/app-ethereum/compare/1.9.11...1.9.12) - 2021-11-12 ### Fixed - Fixed stark order signature on LNX - ## [1.9.11](https://github.com/ledgerhq/app-ethereum/compare/1.9.10...1.9.11) - 2021-10-12 ### Added diff --git a/Makefile b/Makefile index db34aab..a946849 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ APP_LOAD_PARAMS += --path "1517992542'/1101353413'" APPVERSION_M=1 APPVERSION_N=9 -APPVERSION_P=13 +APPVERSION_P=14 APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) APP_LOAD_FLAGS= --appFlags 0x240 --dep Ethereum:$(APPVERSION) From 2c5ae7886c428d8d37521f1269ff97e62cf63075 Mon Sep 17 00:00:00 2001 From: apaillier-ledger <94451027+apaillier-ledger@users.noreply.github.com> Date: Mon, 22 Nov 2021 15:57:10 +0100 Subject: [PATCH 4/9] Update plugin sdk (#220) --- ethereum-plugin-sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethereum-plugin-sdk b/ethereum-plugin-sdk index 682e67a..b653c5e 160000 --- a/ethereum-plugin-sdk +++ b/ethereum-plugin-sdk @@ -1 +1 @@ -Subproject commit 682e67aac9e368caca9ad4a40fc6c65893871504 +Subproject commit b653c5ed1b92b4cf7ba4ca78d1c2c2355ed38bea From 586155c0d4b377f84693367309f89b42e63c20fa Mon Sep 17 00:00:00 2001 From: Francesco <74672573+fcipollone-ledger@users.noreply.github.com> Date: Fri, 10 Dec 2021 14:08:37 +0100 Subject: [PATCH 5/9] Fixed broken link to Nano app intro page on Developer Portal (#221) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1713c74..5fd6b9a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Ledger Blue is not maintained anymore, but the app can still be compiled for thi This app follows the specification available in the `doc/` folder. -To compile it and load it on a device, please check out our [developer portal](https://developers.ledger.com/docs/NA/start_here/). +To compile it and load it on a device, please check out our [developer portal](https://developers.ledger.com/docs/nano-app/introduction/). # Plugins From a53a2428cc024855ef2012ca357e054f838ab962 Mon Sep 17 00:00:00 2001 From: Jean P <10632523+TamtamHero@users.noreply.github.com> Date: Fri, 17 Dec 2021 12:04:51 +0100 Subject: [PATCH 6/9] Fix nft transactions (#229) * Removed pluginType "hack" * Fix some ERC 721 & 1155 function signature hashes * Fix UI for ERC721 operations * Explicit Batch Transfer UI with ERC1155 * Unified some ERC721 & 1155 non-static functions naming * Fix UI for ERC1155 operations * Added missing pin-lock check when signing transactions * Fix the shell script that builds the elf files for testing * Add tests dependency ethers * Removed the space in the test filename * Tests build script refactoring * Now works when called from anywhere (not just the script's directory) * Now handles LNS & LNX builds together (less duplicated code) * Temporarily disable Nano X tests Until Zemu supports Nano X 2.0 SDK * Tests now start with blind signing disabled Makes it closer to reality & very few of them requires it * Update to the latest sdk version * make eth_plugin_perform_init() readable Introduce 2 functions. * Now properly parses the apdu and displays the total quantity of NFT IDs transferred in ERC1155 batch transfer * Add NFT prod public keys * Added extra checks for the chain ID handling Following the security review * NFTs now only supported by LNS * Version bump Co-authored-by: Alexandre Paillier Co-authored-by: greenknot --- CHANGELOG.md | 14 +- Makefile | 6 +- ethereum-plugin-sdk | 2 +- src/eth_plugin_handler.c | 118 +++--- src/main.c | 2 + src/shared_context.h | 3 +- .../cmd_provideNFTInfo.c | 41 +- src_features/setPlugin/cmd_setPlugin.c | 46 ++- src_features/signTx/cmd_signTx.c | 5 + src_plugins/erc1155/erc1155_plugin.c | 14 +- src_plugins/erc1155/erc1155_plugin.h | 22 +- .../erc1155/erc1155_provide_parameters.c | 82 +++- src_plugins/erc1155/erc1155_ui.c | 61 ++- src_plugins/erc721/erc721_plugin.c | 18 +- src_plugins/erc721/erc721_plugin.h | 8 +- .../erc721/erc721_provide_parameters.c | 8 +- src_plugins/erc721/erc721_ui.c | 10 +- tests/build_local_test_elfs.sh | 52 ++- tests/package.json | 1 + .../nanos_enable_blind_signing/00000.png | Bin 0 -> 531 bytes .../nanos_enable_blind_signing/00001.png | Bin 0 -> 344 bytes .../nanos_enable_blind_signing/00002.png | Bin 0 -> 480 bytes .../nanos_enable_blind_signing/00003.png | Bin 0 -> 566 bytes .../nanos_enable_blind_signing/00004.png | Bin 0 -> 514 bytes .../nanos_enable_blind_signing/00005.png | Bin 0 -> 614 bytes .../nanos_enable_blind_signing/00006.png | Bin 0 -> 628 bytes .../nanos_enable_blind_signing/00007.png | Bin 0 -> 338 bytes .../nanos_enable_blind_signing/00008.png | Bin 0 -> 531 bytes tests/src/approve.test.js | 2 +- tests/src/blind_compound_deposit.test.js | 4 +- tests/src/chainid.test.js | 6 +- tests/src/contract_data_warning.test.js | 7 +- tests/src/eip1559.test.js | 4 +- .../src/{erc1155.test .js => erc1155.test.js} | 0 tests/src/erc721.test.js | 10 +- tests/src/send.test.js | 4 +- tests/src/send_bsc.test.js | 4 +- tests/src/send_etc.test.js | 6 +- tests/src/starkware.test.js | 2 +- tests/yarn.lock | 381 ++++++++++++++++++ 40 files changed, 735 insertions(+), 208 deletions(-) create mode 100644 tests/snapshots/nanos_enable_blind_signing/00000.png create mode 100644 tests/snapshots/nanos_enable_blind_signing/00001.png create mode 100644 tests/snapshots/nanos_enable_blind_signing/00002.png create mode 100644 tests/snapshots/nanos_enable_blind_signing/00003.png create mode 100644 tests/snapshots/nanos_enable_blind_signing/00004.png create mode 100644 tests/snapshots/nanos_enable_blind_signing/00005.png create mode 100644 tests/snapshots/nanos_enable_blind_signing/00006.png create mode 100644 tests/snapshots/nanos_enable_blind_signing/00007.png create mode 100644 tests/snapshots/nanos_enable_blind_signing/00008.png rename tests/src/{erc1155.test .js => erc1155.test.js} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index d008b39..0c8def5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [1.9.14](https://github.com/ledgerhq/app-ethereum/compare/1.9.13...1.9.14) - 2021-11-22 +## [1.9.15](https://github.com/ledgerhq/app-ethereum/compare/1.9.14...1.9.15) - 2021-12-15 ### Added -- Support for Non-Fungible Token (ERC 721 & ERC 1155) +- Support for Non-Fungible Token (ERC-721 & ERC-1155) + +## [1.9.14](https://github.com/ledgerhq/app-ethereum/compare/1.9.13...1.9.14) - 2021-11-30 + +### Added + +- Added Moonriver BIP44 1285 + +### Fixed + +- Fixed stark order signature on LNS ## [1.9.13](https://github.com/ledgerhq/app-ethereum/compare/1.9.12...1.9.13) - 2021-11-17 diff --git a/Makefile b/Makefile index a946849..c65535b 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ APP_LOAD_PARAMS += --path "1517992542'/1101353413'" APPVERSION_M=1 APPVERSION_N=9 -APPVERSION_P=14 +APPVERSION_P=15 APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) APP_LOAD_FLAGS= --appFlags 0x240 --dep Ethereum:$(APPVERSION) @@ -306,11 +306,15 @@ ifneq ($(BYPASS_SIGNATURES),0) DEFINES += HAVE_BYPASS_SIGNATURES endif +# NFTs +ifeq ($(TARGET_NAME), TARGET_NANOX) +DEFINES += HAVE_NFT_SUPPORT # Enable the NFT testing key NFT_TESTING_KEY:=0 ifneq ($(NFT_TESTING_KEY),0) DEFINES += HAVE_NFT_TESTING_KEY endif +endif # Enabling debug PRINTF diff --git a/ethereum-plugin-sdk b/ethereum-plugin-sdk index b653c5e..5ade2d7 160000 --- a/ethereum-plugin-sdk +++ b/ethereum-plugin-sdk @@ -1 +1 @@ -Subproject commit b653c5ed1b92b4cf7ba4ca78d1c2c2355ed38bea +Subproject commit 5ade2d77cb6f0f366210d014bc731212a22fc29c diff --git a/src/eth_plugin_handler.c b/src/eth_plugin_handler.c index ccfbab8..a6e685e 100644 --- a/src/eth_plugin_handler.c +++ b/src/eth_plugin_handler.c @@ -72,69 +72,77 @@ void eth_plugin_prepare_query_contract_UI(ethQueryContractUI_t *queryContractUI, queryContractUI->msgLength = msgLength; } +static void eth_plugin_perform_init_default(uint8_t *contractAddress, + ethPluginInitContract_t *init) { + // 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); + } + 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"); + dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_OK; +} + +static bool eth_plugin_perform_init_old_internal(uint8_t *contractAddress, + ethPluginInitContract_t *init) { + uint8_t i, j; + const uint8_t **selectors; + + // Search internal plugin list + for (i = 0;; i++) { + 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; + return true; + } + } + } + } + + return false; +} + eth_plugin_result_t eth_plugin_perform_init(uint8_t *contractAddress, ethPluginInitContract_t *init) { - uint8_t i; - const uint8_t **selectors; dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_UNAVAILABLE; PRINTF("Selector %.*H\n", 4, init->selector); switch (pluginType) { - case NOT_OLD_INTERNAL: +#ifdef HAVE_NFT_SUPPORT 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); - } - 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; +#endif // HAVE_NFT_SUPPORT + case EXTERNAL: + eth_plugin_perform_init_default(contractAddress, init); 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; + case OLD_INTERNAL: + if (eth_plugin_perform_init_old_internal(contractAddress, init)) { + contractAddress = NULL; } - } break; + break; default: PRINTF("Unsupported pluginType %d\n", pluginType); os_sched_exit(0); @@ -242,8 +250,6 @@ eth_plugin_result_t eth_plugin_call(int method, void *parameter) { } switch (pluginType) { - case NOT_OLD_INTERNAL: - break; case EXTERNAL: { uint32_t params[3]; params[0] = (uint32_t) alias; @@ -262,6 +268,7 @@ eth_plugin_result_t eth_plugin_call(int method, void *parameter) { END_TRY; break; } +#ifdef HAVE_NFT_SUPPORT case ERC721: { erc721_plugin_call(method, parameter); break; @@ -270,6 +277,7 @@ eth_plugin_result_t eth_plugin_call(int method, void *parameter) { erc1155_plugin_call(method, parameter); break; } +#endif // HAVE_NFT_SUPPORT case OLD_INTERNAL: { // Perform the call for (i = 0;; i++) { diff --git a/src/main.c b/src/main.c index 05354b9..b677645 100644 --- a/src/main.c +++ b/src/main.c @@ -513,6 +513,7 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { tx); break; +#ifdef HAVE_NFT_SUPPORT case INS_PROVIDE_NFT_INFORMATION: handleProvideNFTInformation(G_io_apdu_buffer[OFFSET_P1], G_io_apdu_buffer[OFFSET_P2], @@ -521,6 +522,7 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { flags, tx); break; +#endif // HAVE_NFT_SUPPORT case INS_SET_EXTERNAL_PLUGIN: handleSetExternalPlugin(G_io_apdu_buffer[OFFSET_P1], diff --git a/src/shared_context.h b/src/shared_context.h index ed15d6c..b5c944f 100644 --- a/src/shared_context.h +++ b/src/shared_context.h @@ -207,8 +207,7 @@ 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. + OLD_INTERNAL // Old internal plugin, not set by any command. } pluginType_t; extern pluginType_t pluginType; diff --git a/src_features/provideNFTInformation/cmd_provideNFTInfo.c b/src_features/provideNFTInformation/cmd_provideNFTInfo.c index 692b551..1495f2c 100644 --- a/src_features/provideNFTInformation/cmd_provideNFTInfo.c +++ b/src_features/provideNFTInformation/cmd_provideNFTInfo.c @@ -1,3 +1,5 @@ +#ifdef HAVE_NFT_SUPPORT + #include "shared_context.h" #include "apdu_constants.h" #include "ui_flow.h" @@ -16,8 +18,8 @@ #define MIN_DER_SIG_SIZE 67 #define MAX_DER_SIG_SIZE 72 -#define TESTING_KEY 0 -#define NFT_METADATA_KEY_1 1 +#define TEST_NFT_METADATA_KEY 0 +#define PROD_NFT_METADATA_KEY 1 #define ALGORITHM_ID_1 1 @@ -25,17 +27,22 @@ #define VERSION_1 1 +static const uint8_t LEDGER_NFT_METADATA_PUBLIC_KEY[] = { #ifdef HAVE_NFT_TESTING_KEY -static const uint8_t LEDGER_NFT_PUBLIC_KEY[] = { 0x04, 0xf5, 0x70, 0x0c, 0xa1, 0xe8, 0x74, 0x24, 0xc7, 0xc7, 0xd1, 0x19, 0xe7, 0xe3, 0xc1, 0x89, 0xb1, 0x62, 0x50, 0x94, 0xdb, 0x6e, 0xa0, 0x40, 0x87, 0xc8, 0x30, 0x00, 0x7d, 0x0b, 0x46, 0x9a, 0x53, 0x11, 0xee, 0x6a, 0x1a, 0xcd, 0x1d, 0xa5, 0xaa, 0xb0, 0xf5, 0xc6, 0xdf, 0x13, 0x15, 0x8d, 0x28, 0xcc, 0x12, 0xd1, - 0xdd, 0xa6, 0xec, 0xe9, 0x46, 0xb8, 0x9d, 0x5c, 0x05, 0x49, 0x92, 0x59, 0xc4}; + 0xdd, 0xa6, 0xec, 0xe9, 0x46, 0xb8, 0x9d, 0x5c, 0x05, 0x49, 0x92, 0x59, 0xc4 #else -static const uint8_t LEDGER_NFT_PUBLIC_KEY[] = {}; + 0x04, 0x98, 0x8d, 0xa6, 0xb2, 0x46, 0xf2, 0x8e, 0x77, 0xc1, 0xba, 0xb6, 0x75, + 0xcb, 0x2a, 0x27, 0x44, 0xf7, 0xf5, 0xce, 0xc5, 0x6a, 0xe6, 0xe0, 0x32, 0x23, + 0x33, 0x7b, 0x57, 0x94, 0xcd, 0x6a, 0xe0, 0x7d, 0x48, 0xb3, 0x0d, 0xb9, 0xcc, + 0xb4, 0x0f, 0x5a, 0x02, 0xa1, 0x1a, 0x3a, 0xb9, 0x9d, 0x5f, 0x59, 0x5a, 0x3d, + 0x50, 0xa0, 0xe1, 0x30, 0x23, 0xfd, 0x0d, 0x95, 0x87, 0x92, 0xd7, 0x97, 0x01 #endif +}; typedef bool verificationAlgo(const cx_ecfp_public_key_t *, int, @@ -129,9 +136,14 @@ void handleProvideNFTInformation(uint8_t p1, PRINTF("Address: %.*H\n", ADDRESS_LENGTH, workBuffer + offset); offset += ADDRESS_LENGTH; - // TODO: store chainID and assert that tx is using the same chainid. - // uint64_t chainid = u64_from_BE(workBuffer + offset, CHAIN_ID_SIZE); - // PRINTF("ChainID: %.*H\n", sizeof(chainid), &chainid); + uint64_t chainId = u64_from_BE(workBuffer + offset, CHAIN_ID_SIZE); + // this prints raw data, so to have a more meaningful print, display + // the buffer before the endianness swap + PRINTF("ChainID: %.*H\n", sizeof(chainId), (workBuffer + offset)); + if ((chainConfig->chainId != 0) && (chainConfig->chainId != chainId)) { + PRINTF("Chain ID token mismatch\n"); + THROW(0x6A80); + } offset += CHAIN_ID_SIZE; uint8_t keyId = workBuffer[offset]; @@ -141,11 +153,11 @@ void handleProvideNFTInformation(uint8_t p1, PRINTF("KeyID: %d\n", keyId); switch (keyId) { #ifdef HAVE_NFT_TESTING_KEY - case TESTING_KEY: + case TEST_NFT_METADATA_KEY: #endif - case NFT_METADATA_KEY_1: - rawKey = (uint8_t *) LEDGER_NFT_PUBLIC_KEY; - rawKeyLen = sizeof(LEDGER_NFT_PUBLIC_KEY); + case PROD_NFT_METADATA_KEY: + rawKey = (uint8_t *) LEDGER_NFT_METADATA_PUBLIC_KEY; + rawKeyLen = sizeof(LEDGER_NFT_METADATA_PUBLIC_KEY); break; default: PRINTF("KeyID %d not supported\n", keyId); @@ -210,10 +222,9 @@ void handleProvideNFTInformation(uint8_t p1, THROW(0x6A80); #endif } - // Set this to `NOT_OLD_INTERNAL` because otherwise the tx might be treated as an - // internal plugin and we might get a collision and hence some BIG problems. - pluginType = NOT_OLD_INTERNAL; tmpCtx.transactionContext.tokenSet[tmpCtx.transactionContext.currentItemIndex] = 1; THROW(0x9000); } + +#endif // HAVE_NFT_SUPPORT diff --git a/src_features/setPlugin/cmd_setPlugin.c b/src_features/setPlugin/cmd_setPlugin.c index a81e9c9..ea39aa2 100644 --- a/src_features/setPlugin/cmd_setPlugin.c +++ b/src_features/setPlugin/cmd_setPlugin.c @@ -4,6 +4,7 @@ #include "tokens.h" #include "eth_plugin_interface.h" #include "eth_plugin_internal.h" +#include "utils.h" // Supported internal plugins #define ERC721_STR "ERC721" @@ -31,10 +32,9 @@ typedef enum Version { } Version; typedef enum KeyId { - TEST_KEY = 0x00, - PERSO_V2_KEY_1 = 0x01, + TEST_PLUGIN_KEY = 0x00, // Must ONLY be used with ERC721 and ERC1155 plugin - AWS_PLUGIN_KEY_1 = 0x02, + PROD_PLUGIN_KEY = 0x02, } KeyId; // Algorithm Id consists of a Key spec and an algorithm spec. @@ -43,18 +43,22 @@ typedef enum AlgorithmID { ECC_SECG_P256K1__ECDSA_SHA_256 = 0x01, } AlgorithmID; +// Only used for signing NFT plugins (ERC721 and ERC1155) +static const uint8_t LEDGER_NFT_SELECTOR_PUBLIC_KEY[] = { #ifdef HAVE_NFT_TESTING_KEY -static const uint8_t LEDGER_TESTING_KEY[] = { 0x04, 0xf5, 0x70, 0x0c, 0xa1, 0xe8, 0x74, 0x24, 0xc7, 0xc7, 0xd1, 0x19, 0xe7, 0xe3, 0xc1, 0x89, 0xb1, 0x62, 0x50, 0x94, 0xdb, 0x6e, 0xa0, 0x40, 0x87, 0xc8, 0x30, 0x00, 0x7d, 0x0b, 0x46, 0x9a, 0x53, 0x11, 0xee, 0x6a, 0x1a, 0xcd, 0x1d, 0xa5, 0xaa, 0xb0, 0xf5, 0xc6, 0xdf, 0x13, 0x15, 0x8d, 0x28, 0xcc, 0x12, 0xd1, - 0xdd, 0xa6, 0xec, 0xe9, 0x46, 0xb8, 0x9d, 0x5c, 0x05, 0x49, 0x92, 0x59, 0xc4}; + 0xdd, 0xa6, 0xec, 0xe9, 0x46, 0xb8, 0x9d, 0x5c, 0x05, 0x49, 0x92, 0x59, 0xc4 +#else + 0x04, 0xd8, 0x62, 0x6e, 0x01, 0x9e, 0x55, 0x3e, 0x19, 0x69, 0x56, 0xf1, 0x17, + 0x4d, 0xcd, 0xb8, 0x9a, 0x1c, 0xda, 0xc4, 0x93, 0x90, 0x08, 0xbc, 0x79, 0x77, + 0x33, 0x6d, 0x78, 0x24, 0xee, 0xe3, 0xa2, 0x62, 0x24, 0x1a, 0x62, 0x73, 0x52, + 0x3b, 0x09, 0xb8, 0xd0, 0xce, 0x0d, 0x39, 0xe8, 0x60, 0xc9, 0x4d, 0x02, 0x53, + 0x58, 0xdb, 0xdc, 0x25, 0x92, 0xc7, 0xc6, 0x48, 0x0d, 0x39, 0xce, 0xbb, 0xa3 #endif -static const uint8_t LEDGER_PERSO_V2_PUBLIC_KEY[] = {}; - -// Only used for signing NFT plugins (ERC721 and ERC1155) -static const uint8_t LEDGER_NFT_SELECTOR_PUBLIC_KEY[] = {}; +}; // Verification function used to verify the signature typedef bool verificationAlgo(const cx_ecfp_public_key_t *, @@ -164,9 +168,14 @@ void handleSetPlugin(uint8_t p1, PRINTF("Selector: %.*H\n", SELECTOR_SIZE, tokenContext->methodSelector); offset += SELECTOR_SIZE; - // TODO: store chainID and assert that tx is using the same chainid. - // uint64_t chainid = u64_from_BE(workBuffer + offset, CHAIN_ID_SIZE); - // PRINTF("ChainID: %.*H\n", sizeof(chainid), &chainid); + uint64_t chainId = u64_from_BE(workBuffer + offset, CHAIN_ID_SIZE); + // this prints raw data, so to have a more meaningful print, display + // the buffer before the endianness swap + PRINTF("ChainID: %.*H\n", sizeof(chainId), (workBuffer + offset)); + if ((chainConfig->chainId != 0) && (chainConfig->chainId != chainId)) { + PRINTF("Chain ID token mismatch\n"); + THROW(0x6A80); + } offset += CHAIN_ID_SIZE; enum KeyId keyId = workBuffer[offset]; @@ -176,16 +185,9 @@ void handleSetPlugin(uint8_t p1, PRINTF("KeyID: %d\n", keyId); switch (keyId) { #ifdef HAVE_NFT_TESTING_KEY - case TEST_KEY: - rawKey = LEDGER_TESTING_KEY; - rawKeyLen = sizeof(LEDGER_TESTING_KEY); - break; + case TEST_PLUGIN_KEY: #endif - case PERSO_V2_KEY_1: - rawKey = LEDGER_PERSO_V2_PUBLIC_KEY; - rawKeyLen = sizeof(LEDGER_PERSO_V2_PUBLIC_KEY); - break; - case AWS_PLUGIN_KEY_1: + case PROD_PLUGIN_KEY: rawKey = LEDGER_NFT_SELECTOR_PUBLIC_KEY; rawKeyLen = sizeof(LEDGER_NFT_SELECTOR_PUBLIC_KEY); break; @@ -255,7 +257,7 @@ void handleSetPlugin(uint8_t p1, } pluginType = getPluginType(tokenContext->pluginName, pluginNameLength); - if (keyId == AWS_PLUGIN_KEY_1) { + if (keyId == PROD_PLUGIN_KEY) { if (pluginType != ERC721 && pluginType != ERC1155) { PRINTF("AWS key must only be used to set NFT internal plugins\n"); THROW(0x6A80); diff --git a/src_features/signTx/cmd_signTx.c b/src_features/signTx/cmd_signTx.c index 072abc5..3b084f5 100644 --- a/src_features/signTx/cmd_signTx.c +++ b/src_features/signTx/cmd_signTx.c @@ -13,6 +13,11 @@ void handleSign(uint8_t p1, UNUSED(tx); parserStatus_e txResult; uint32_t i; + + if (os_global_pin_is_validated() != BOLOS_UX_OK) { + PRINTF("Device is PIN-locked"); + THROW(0x6982); + } if (p1 == P1_FIRST) { if (dataLength < 1) { PRINTF("Invalid data\n"); diff --git a/src_plugins/erc1155/erc1155_plugin.c b/src_plugins/erc1155/erc1155_plugin.c index 20b6953..7eebd8c 100644 --- a/src_plugins/erc1155/erc1155_plugin.c +++ b/src_plugins/erc1155/erc1155_plugin.c @@ -1,9 +1,11 @@ +#ifdef HAVE_NFT_SUPPORT + #include "erc1155_plugin.h" #include "eth_plugin_internal.h" static const uint8_t ERC1155_APPROVE_FOR_ALL_SELECTOR[SELECTOR_SIZE] = {0xa2, 0x2c, 0xb4, 0x65}; static const uint8_t ERC1155_SAFE_TRANSFER_SELECTOR[SELECTOR_SIZE] = {0xf2, 0x42, 0x43, 0x2a}; -static const uint8_t ERC1155_SAFE_BATCH_TRANSFER[SELECTOR_SIZE] = {0xf2, 0x42, 0x43, 0x2a}; +static const uint8_t ERC1155_SAFE_BATCH_TRANSFER[SELECTOR_SIZE] = {0x2e, 0xb2, 0xc2, 0xd6}; const uint8_t *const ERC1155_SELECTORS[NUM_ERC1155_SELECTORS] = { ERC1155_APPROVE_FOR_ALL_SELECTOR, @@ -59,11 +61,11 @@ static void handle_finalize(void *parameters) { msg->tokenLookup2 = NULL; switch (context->selectorIndex) { case SAFE_TRANSFER: - msg->numScreens = 4; + msg->numScreens = 5; break; case SET_APPROVAL_FOR_ALL: case SAFE_BATCH_TRANSFER: - msg->numScreens = 3; + msg->numScreens = 4; break; default: msg->result = ETH_PLUGIN_RESULT_ERROR; @@ -99,9 +101,11 @@ static void handle_query_contract_id(void *parameters) { strlcpy(msg->version, "Allowance", msg->versionLength); break; case SAFE_TRANSFER: - case SAFE_BATCH_TRANSFER: strlcpy(msg->version, "Transfer", msg->versionLength); break; + case SAFE_BATCH_TRANSFER: + strlcpy(msg->version, "Batch Transfer", msg->versionLength); + break; default: PRINTF("Unsupported selector %d\n", context->selectorIndex); msg->result = ETH_PLUGIN_RESULT_ERROR; @@ -134,3 +138,5 @@ void erc1155_plugin_call(int message, void *parameters) { break; } } + +#endif // HAVE_NFT_SUPPORT diff --git a/src_plugins/erc1155/erc1155_plugin.h b/src_plugins/erc1155/erc1155_plugin.h index fefd165..22462a8 100644 --- a/src_plugins/erc1155/erc1155_plugin.h +++ b/src_plugins/erc1155/erc1155_plugin.h @@ -1,3 +1,5 @@ +#ifdef HAVE_NFT_SUPPORT + #pragma once #include @@ -5,6 +7,7 @@ #include "shared_context.h" #include "ethUtils.h" #include "utils.h" +#include "uint256.h" // Internal plugin for EIP 1155: https://eips.ethereum.org/EIPS/eip-1155 @@ -31,19 +34,22 @@ typedef enum { } erc1155_selector_field; typedef struct erc1155_context_t { - uint8_t address[ADDRESS_LENGTH]; - uint8_t tokenId[INT256_LENGTH]; - uint8_t value[INT256_LENGTH]; + uint8_t address[ADDRESS_LENGTH]; + uint8_t tokenId[INT256_LENGTH]; + uint256_t value; - uint32_t valueOffset; - uint32_t tokenIdsOffset; - uint32_t targetOffset; + uint16_t ids_array_len; + uint32_t ids_offset; + uint16_t values_array_len; + uint32_t values_offset; + uint16_t array_index; bool approved; erc1155_selector_field next_param; uint8_t selectorIndex; } erc1155_context_t; -// TODO: Find out why there is a duplicate if we remove 1155 suffix void handle_provide_parameter_1155(void *parameters); -void handle_query_contract_ui_1155(void *parameters); \ No newline at end of file +void handle_query_contract_ui_1155(void *parameters); + +#endif // HAVE_NFT_SUPPORT diff --git a/src_plugins/erc1155/erc1155_provide_parameters.c b/src_plugins/erc1155/erc1155_provide_parameters.c index 8bbfb79..59da428 100644 --- a/src_plugins/erc1155/erc1155_provide_parameters.c +++ b/src_plugins/erc1155/erc1155_provide_parameters.c @@ -1,7 +1,11 @@ +#ifdef HAVE_NFT_SUPPORT + #include "erc1155_plugin.h" #include "eth_plugin_internal.h" static void handle_safe_transfer(ethPluginProvideParameter_t *msg, erc1155_context_t *context) { + uint8_t new_value[INT256_LENGTH]; + switch (context->next_param) { case FROM: context->next_param = TO; @@ -15,7 +19,8 @@ static void handle_safe_transfer(ethPluginProvideParameter_t *msg, erc1155_conte context->next_param = VALUE; break; case VALUE: - copy_parameter(context->value, msg->parameter, sizeof(context->value)); + copy_parameter(new_value, msg->parameter, sizeof(new_value)); + convertUint256BE(new_value, INT256_LENGTH, &context->value); context->next_param = NONE; break; default: @@ -25,31 +30,74 @@ static void handle_safe_transfer(ethPluginProvideParameter_t *msg, erc1155_conte } static void handle_batch_transfer(ethPluginProvideParameter_t *msg, erc1155_context_t *context) { + uint256_t new_value; + switch (context->next_param) { case FROM: context->next_param = TO; break; case TO: copy_address(context->address, msg->parameter, sizeof(context->address)); - context->next_param = TOKEN_ID; + context->next_param = TOKEN_IDS_OFFSET; break; case TOKEN_IDS_OFFSET: - context->tokenIdsOffset = U4BE(msg->parameter, PARAMETER_LENGTH - 4); + context->ids_offset = \ + U4BE(msg->parameter, + PARAMETER_LENGTH - sizeof(context->ids_offset)) + 4; context->next_param = VALUE_OFFSET; break; case VALUE_OFFSET: - context->targetOffset = context->tokenIdsOffset; - context->next_param = TOKEN_ID; + context->values_offset = \ + U4BE(msg->parameter, + PARAMETER_LENGTH - sizeof(context->values_offset)) + 4; + context->next_param = TOKEN_IDS_LENGTH; + break; + case TOKEN_IDS_LENGTH: + if ((msg->parameterOffset + PARAMETER_LENGTH) > context->ids_offset) + { + context->ids_array_len = \ + U2BE(msg->parameter, + PARAMETER_LENGTH - sizeof(context->ids_array_len)); + context->next_param = TOKEN_ID; + // set to zero for next step + context->array_index = 0; + } break; case TOKEN_ID: - copy_parameter(context->tokenId, msg->parameter, sizeof(context->tokenId)); - context->targetOffset = context->valueOffset; - context->next_param = VALUE; + // don't copy anything since we won't display it + if (--context->ids_array_len == 0) + { + context->next_param = VALUE_LENGTH; + } + context->array_index++; + break; + case VALUE_LENGTH: + if ((msg->parameterOffset + PARAMETER_LENGTH) > context->values_offset) + { + context->values_array_len = \ + U2BE(msg->parameter, + PARAMETER_LENGTH - sizeof(context->values_array_len)); + if (context->values_array_len != context->array_index) + { + PRINTF("Token ids and values array sizes mismatch!"); + } + context->next_param = VALUE; + // set to zero for next step + context->array_index = 0; + explicit_bzero(&context->value, sizeof(context->value)); + } break; case VALUE: - copy_parameter(context->value, msg->parameter, sizeof(context->value)); - context->targetOffset = 0; - context->next_param = NONE; + // put it temporarily in token id since we don't use it in batch transfer + copy_parameter(context->tokenId, msg->parameter, sizeof(context->value)); + convertUint256BE(context->tokenId, sizeof(context->tokenId), &new_value); + add256(&context->value, &new_value, &context->value); + if (--context->values_array_len == 0) + { + context->next_param = NONE; + } + context->array_index++; + break; default: // Some extra data might be present so don't error. break; @@ -84,10 +132,10 @@ void handle_provide_parameter_1155(void *parameters) { msg->result = ETH_PLUGIN_RESULT_SUCCESSFUL; - if (context->targetOffset > SELECTOR_SIZE && - context->targetOffset != msg->parameterOffset - SELECTOR_SIZE) { - return; - } + //if (context->targetOffset > SELECTOR_SIZE && + // context->targetOffset != msg->parameterOffset - SELECTOR_SIZE) { + // return; + //} switch (context->selectorIndex) { case SAFE_TRANSFER: handle_safe_transfer(msg, context); @@ -103,4 +151,6 @@ void handle_provide_parameter_1155(void *parameters) { msg->result = ETH_PLUGIN_RESULT_ERROR; break; } -} \ No newline at end of file +} + +#endif // HAVE_NFT_SUPPORT diff --git a/src_plugins/erc1155/erc1155_ui.c b/src_plugins/erc1155/erc1155_ui.c index 2885233..5237a16 100644 --- a/src_plugins/erc1155/erc1155_ui.c +++ b/src_plugins/erc1155/erc1155_ui.c @@ -1,3 +1,5 @@ +#ifdef HAVE_NFT_SUPPORT + #include "erc1155_plugin.h" static void set_approval_for_all_ui(ethQueryContractUI_t *msg, erc1155_context_t *context) { @@ -40,20 +42,6 @@ static void set_approval_for_all_ui(ethQueryContractUI_t *msg, erc1155_context_t static void set_transfer_ui(ethQueryContractUI_t *msg, erc1155_context_t *context) { switch (msg->screenIndex) { case 0: - // What will be displayed on the screen is: - // | Send | - // | `X` `COLLECTION_NAME` | - // where `X` is `value` - strlcpy(msg->title, "Send", msg->titleLength); - uint256_to_decimal(context->value, sizeof(context->value), msg->msg, msg->msgLength); - strlcat(msg->msg, " ", msg->msgLength); - if (msg->item1) { - strlcat(msg->msg, (const char *) &msg->item1->nft.collectionName, msg->msgLength); - } else { - strlcat(msg->msg, "Items", msg->msgLength); - } - break; - case 1: strlcpy(msg->title, "To", msg->titleLength); getEthDisplayableAddress(context->address, msg->msg, @@ -61,9 +49,17 @@ static void set_transfer_ui(ethQueryContractUI_t *msg, erc1155_context_t *contex &global_sha3, chainConfig->chainId); break; + case 1: + strlcpy(msg->title, "Collection Name", msg->titleLength); + if (msg->item1) { + strlcpy(msg->msg, (const char *) &msg->item1->nft.collectionName, msg->msgLength); + } else { + strlcpy(msg->msg, "Not Found", msg->msgLength); + } + break; case 2: strlcpy(msg->title, "NFT Address", msg->titleLength); - getEthDisplayableAddress(msg->pluginSharedRO->txContent->destination, + getEthDisplayableAddress((uint8_t *)msg->item1->nft.contractAddress, msg->msg, msg->msgLength, &global_sha3, @@ -76,6 +72,10 @@ static void set_transfer_ui(ethQueryContractUI_t *msg, erc1155_context_t *contex msg->msg, msg->msgLength); break; + case 4: + strlcpy(msg->title, "Quantity", msg->titleLength); + tostring256(&context->value, 10, msg->msg, msg->msgLength); + break; default: PRINTF("Unsupported screen index %d\n", msg->screenIndex); msg->result = ETH_PLUGIN_RESULT_ERROR; @@ -84,27 +84,42 @@ static void set_transfer_ui(ethQueryContractUI_t *msg, erc1155_context_t *contex } static void set_batch_transfer_ui(ethQueryContractUI_t *msg, erc1155_context_t *context) { + char quantity_str[48]; + switch (msg->screenIndex) { case 0: - strlcpy(msg->title, "Send NFTs From", msg->titleLength); - uint256_to_decimal(context->value, sizeof(context->value), msg->msg, msg->msgLength); - strlcat(msg->msg, " Different Collections", msg->msgLength); - break; - case 1: strlcpy(msg->title, "To", msg->titleLength); getEthDisplayableAddress(context->address, msg->msg, msg->msgLength, &global_sha3, chainConfig->chainId); + break; + case 1: + strlcpy(msg->title, "Collection Name", msg->titleLength); + if (msg->item1) { + strlcpy(msg->msg, (const char *) &msg->item1->nft.collectionName, msg->msgLength); + } else { + strlcpy(msg->msg, "Not Found", msg->msgLength); + } + break; case 2: strlcpy(msg->title, "NFT Address", msg->titleLength); - getEthDisplayableAddress(msg->pluginSharedRO->txContent->destination, + getEthDisplayableAddress((uint8_t *)msg->item1->nft.contractAddress, msg->msg, msg->msgLength, &global_sha3, chainConfig->chainId); break; + case 3: + strlcpy(msg->title, "Total Quantity", msg->titleLength); + tostring256(&context->value, 10, &quantity_str[0], sizeof(quantity_str)); + snprintf(msg->msg, + msg->msgLength, + "%s from %d NFT IDs", + quantity_str, + context->array_index); + break; default: PRINTF("Unsupported screen index %d\n", msg->screenIndex); msg->result = ETH_PLUGIN_RESULT_ERROR; @@ -132,4 +147,6 @@ void handle_query_contract_ui_1155(void *parameters) { PRINTF("Unsupported selector index %d\n", context->selectorIndex); break; } -} \ No newline at end of file +} + +#endif // HAVE_NFT_SUPPORT diff --git a/src_plugins/erc721/erc721_plugin.c b/src_plugins/erc721/erc721_plugin.c index c9542d8..4bdd232 100644 --- a/src_plugins/erc721/erc721_plugin.c +++ b/src_plugins/erc721/erc721_plugin.c @@ -1,11 +1,13 @@ +#ifdef HAVE_NFT_SUPPORT + #include "erc721_plugin.h" #include "eth_plugin_internal.h" -static const uint8_t ERC721_APPROVE_SELECTOR[SELECTOR_SIZE] = {0x13, 0x37, 0x42, 0x42}; +static const uint8_t ERC721_APPROVE_SELECTOR[SELECTOR_SIZE] = {0x09, 0x5e, 0xa7, 0xb3}; static const uint8_t ERC721_APPROVE_FOR_ALL_SELECTOR[SELECTOR_SIZE] = {0xa2, 0x2c, 0xb4, 0x65}; static const uint8_t ERC721_TRANSFER_SELECTOR[SELECTOR_SIZE] = {0x23, 0xb8, 0x72, 0xdd}; static const uint8_t ERC721_SAFE_TRANSFER_SELECTOR[SELECTOR_SIZE] = {0x42, 0x84, 0x2e, 0x0e}; -static const uint8_t ERC721_SAFE_TRANSFER_DATA_SELECTOR[SELECTOR_SIZE] = {0xf2, 0x42, 0x43, 0x2a}; +static const uint8_t ERC721_SAFE_TRANSFER_DATA_SELECTOR[SELECTOR_SIZE] = {0xb8, 0x8d, 0x4f, 0xde}; const uint8_t *const ERC721_SELECTORS[NUM_ERC721_SELECTORS] = { ERC721_APPROVE_SELECTOR, @@ -62,12 +64,12 @@ static void handle_finalize(void *parameters) { case TRANSFER: case SAFE_TRANSFER: case SAFE_TRANSFER_DATA: - case SET_APPROVAL_FOR_ALL: - msg->numScreens = 3; - break; case APPROVE: msg->numScreens = 4; break; + case SET_APPROVAL_FOR_ALL: + msg->numScreens = 3; + break; default: msg->result = ETH_PLUGIN_RESULT_ERROR; return; @@ -125,7 +127,7 @@ void erc721_plugin_call(int message, void *parameters) { handle_init_contract(parameters); } break; case ETH_PLUGIN_PROVIDE_PARAMETER: { - handle_provide_parameter(parameters); + handle_provide_parameter_721(parameters); } break; case ETH_PLUGIN_FINALIZE: { handle_finalize(parameters); @@ -137,10 +139,12 @@ void erc721_plugin_call(int message, void *parameters) { handle_query_contract_id(parameters); } break; case ETH_PLUGIN_QUERY_CONTRACT_UI: { - handle_query_contract_ui(parameters); + handle_query_contract_ui_721(parameters); } break; default: PRINTF("Unhandled message %d\n", message); break; } } + +#endif // HAVE_NFT_SUPPORT diff --git a/src_plugins/erc721/erc721_plugin.h b/src_plugins/erc721/erc721_plugin.h index 235b585..2ab80df 100644 --- a/src_plugins/erc721/erc721_plugin.h +++ b/src_plugins/erc721/erc721_plugin.h @@ -1,3 +1,5 @@ +#ifdef HAVE_NFT_SUPPORT + #pragma once #include @@ -38,5 +40,7 @@ typedef struct erc721_context_t { uint8_t selectorIndex; } erc721_context_t; -void handle_provide_parameter(void *parameters); -void handle_query_contract_ui(void *parameters); \ No newline at end of file +void handle_provide_parameter_721(void *parameters); +void handle_query_contract_ui_721(void *parameters); + +#endif // HAVE_NFT_SUPPORT diff --git a/src_plugins/erc721/erc721_provide_parameters.c b/src_plugins/erc721/erc721_provide_parameters.c index 76e1ea5..a315af0 100644 --- a/src_plugins/erc721/erc721_provide_parameters.c +++ b/src_plugins/erc721/erc721_provide_parameters.c @@ -1,3 +1,5 @@ +#ifdef HAVE_NFT_SUPPORT + #include "erc721_plugin.h" #include "eth_plugin_internal.h" @@ -60,7 +62,7 @@ static void handle_approval_for_all(ethPluginProvideParameter_t *msg, erc721_con } } -void handle_provide_parameter(void *parameters) { +void handle_provide_parameter_721(void *parameters) { ethPluginProvideParameter_t *msg = (ethPluginProvideParameter_t *) parameters; erc721_context_t *context = (erc721_context_t *) msg->pluginContext; @@ -90,4 +92,6 @@ void handle_provide_parameter(void *parameters) { msg->result = ETH_PLUGIN_RESULT_ERROR; break; } -} \ No newline at end of file +} + +#endif // HAVE_NFT_SUPPORT diff --git a/src_plugins/erc721/erc721_ui.c b/src_plugins/erc721/erc721_ui.c index fefa3cf..bb8264c 100644 --- a/src_plugins/erc721/erc721_ui.c +++ b/src_plugins/erc721/erc721_ui.c @@ -1,3 +1,5 @@ +#ifdef HAVE_NFT_SUPPORT + #include "erc721_plugin.h" static void set_approval_ui(ethQueryContractUI_t *msg, erc721_context_t *context) { @@ -113,7 +115,7 @@ static void set_transfer_ui(ethQueryContractUI_t *msg, erc721_context_t *context break; case 2: strlcpy(msg->title, "NFT Address", msg->titleLength); - getEthDisplayableAddress(msg->pluginSharedRO->txContent->destination, + getEthDisplayableAddress((uint8_t *)msg->item1->nft.contractAddress, msg->msg, msg->msgLength, &global_sha3, @@ -141,7 +143,7 @@ static void set_transfer_ui(ethQueryContractUI_t *msg, erc721_context_t *context } } -void handle_query_contract_ui(void *parameters) { +void handle_query_contract_ui_721(void *parameters) { ethQueryContractUI_t *msg = (ethQueryContractUI_t *) parameters; erc721_context_t *context = (erc721_context_t *) msg->pluginContext; @@ -163,4 +165,6 @@ void handle_query_contract_ui(void *parameters) { PRINTF("Unsupported selector index %d\n", context->selectorIndex); break; } -} \ No newline at end of file +} + +#endif // HAVE_NFT_SUPPORT diff --git a/tests/build_local_test_elfs.sh b/tests/build_local_test_elfs.sh index 546690f..ab9a264 100755 --- a/tests/build_local_test_elfs.sh +++ b/tests/build_local_test_elfs.sh @@ -1,34 +1,44 @@ -#!/bin/bash +#!/usr/bin/env bash + +TESTS_FULL_PATH=$(dirname "$(realpath "$0")") # FILL THESE WITH YOUR OWN SDKs PATHS -NANOS_SDK=$TWO -NANOX_SDK=$X +# NANOS_SDK= +# NANOX_SDK= # list of apps required by tests that we want to build here -appnames=("ethereum") +APPS=("ethereum" "ethereum_classic") -# create elfs folder if it doesn't exist +# list of SDKS +NANO_SDKS=("$NANOS_SDK" "$NANOX_SDK") +# list of target elf file name suffix +FILE_SUFFIXES=("nanos" "nanox") + +# move to the tests directory +cd "$TESTS_FULL_PATH" || exit 1 + +# Do it only now since before the cd command, we might not have been inside the repository +GIT_REPO_ROOT=$(git rev-parse --show-toplevel) +TESTS_REL_PATH=$(realpath --relative-to="$GIT_REPO_ROOT" "$TESTS_FULL_PATH") + +# create elfs directory if it doesn't exist mkdir -p elfs # move to repo's root to build apps -cd .. +cd "$GIT_REPO_ROOT" || exit 1 -echo "*Building elfs for Nano S..." -for app in "${appnames[@]}" +for ((sdk_idx=0; sdk_idx < "${#NANO_SDKS[@]}"; sdk_idx++)) do - echo "**Building $app for Nano S..." - make clean BOLOS_SDK=$NANOS_SDK - make -j ALLOW_DATA=1 NFT_TESTING_KEY=1 DEBUG=1 BOLOS_SDK=$NANOS_SDK CHAIN=$app - cp bin/app.elf "tests/elfs/${app}_nanos.elf" + nano_sdk="${NANO_SDKS[$sdk_idx]}" + elf_suffix="${FILE_SUFFIXES[$sdk_idx]}" + echo "* Building elfs for $(basename "$nano_sdk")..." + for appname in "${APPS[@]}" + do + echo "** Building app $appname..." + make clean BOLOS_SDK="$nano_sdk" + make -j DEBUG=1 NFT_TESTING_KEY=1 BOLOS_SDK="$nano_sdk" CHAIN="$appname" + cp bin/app.elf "$TESTS_REL_PATH/elfs/${appname}_${elf_suffix}.elf" + done done -# echo "*Building elfs for Nano X..." -# for app in "${appnames[@]}" -# do -# echo "**Building $app for Nano X..." -# make clean BOLOS_SDK=$NANOX_SDK -# make -j DEBUG=1 BOLOS_SDK=$NANOX_SDK CHAIN=$app -# cp bin/app.elf "tests/elfs/${app}_nanox.elf" -# done - echo "done" diff --git a/tests/package.json b/tests/package.json index ffc9815..98b51ae 100644 --- a/tests/package.json +++ b/tests/package.json @@ -20,6 +20,7 @@ "bip32-path": "^0.4.2", "core-js": "^3.7.0", "ethereum-tx-decoder": "^3.0.0", + "ethers": "^5.5.1", "fs-extra": "^10.0.0", "google-protobuf": "^3.11.0", "jest-serial-runner": "^1.1.0", diff --git a/tests/snapshots/nanos_enable_blind_signing/00000.png b/tests/snapshots/nanos_enable_blind_signing/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..0bef4f3a7b4f6195c1d9fc70dcd11adb9d29bb52 GIT binary patch literal 531 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfiWoba4!+V0?S@KDXZ;sPq04($@Y}pt$UkYuJ7O_vMLEmi+4c;Fw95@=>aR7Nxv47?$ZTZtr_3n+(gx*XR_$~kWo+{1#Gb1SXD%3= zxUfOl`S7wtWoKq1=fesh37M0pZl2)bJ;7rH6tf4a>N|5`!^DM*Gq+1kkOZQXHnx<$ zq^B8D6K1Bx%Ze~iP)OohG zIi_9y5O&xt%^k>mro%ENocDx`AG1-use^*|uq^-bNuC4jCC(rnYh>Cs(MmB?|tDnm{ Hr-UW|v$x^l literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_enable_blind_signing/00001.png b/tests/snapshots/nanos_enable_blind_signing/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..29a6bd33223391861f4cf659bdbf31ac9392613e GIT binary patch literal 344 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfht{x;TbZFupzLm?!Nhz;bX+ zzp$UKM09k8SBL69{CgK>VNLb9_O3Le=&6RZQVNyV0v`w(%8H*4e zt$Dg}D;3gY)YwGVv$R~irtc^E+3l!(=gp8gehwDCXO=xuObh;aALy3lktL$eXT+S( zIP(}rg9!D=p7|>)0vfv}>$qiiU%ZvHWt+vsF5NZ>pyK?bfRD*5Z-IoQ%;uFG&(&l) zl_kZO5~jLf&7z0>DPhVB)+|VzWSKD|C2Z41shQ?Rt8Ya(9#$|myXTf={D$wP-{ z>oX)kZkhwKF_*Xb%oT$ZcOEorXIM;Fn3z>@+2F(lgA+WwKoaP<`I8m`bu7PaF}tm~ z&5>>9LdKb%Y?3Kzk}3PHG@O~9qBfIRGUZ&tW1W;`Z9v@%9=c~Nc*tXR&Lu6&VuB-3 z<-eUkKLP#zJ*1LMp`^|cd@yp6iTF8AdEyXT#C&JL9s-EzMaI&Yi${8`9< zN6*#4GU#yInMI5>pk!>KusI`QqNJ2rN}oY78*lTO*(Z2-K?)?L{&_tMHR@(d zVVk*-ab~QMGqX`M$fm_Ua~gS!PF(nqE441~*(#v1C*EX5oT%B^)_P`Q+B&~BwiL6J zd2Y{m(#|b-cp9kAA86{jHpge)raVUOf&qEiyeHNGotX)tLU>Q?F)7^5CMgvs6p$AU zbYuLoMAv7xfhuM<6z!hImh#WI5Jc6@-Vy80Y*atXGKd-I51=!pjhum=cRSp5rrlIw z^J>PK<(Ur8R!U8%%zTysa+E{bV#b-RAUB7fjG1<3-ps`(FEpG{lnP5}YZINhFmb}q z)oqSx85YemwKF9k-fTSs4U~%wXBH)9H~?i&osm2N)E0blkN4r_rgLtdd0T4K-NrxB za&zbA2@4;7+naXIrLE1et&MjkKhT(aOA^BmD{S@#3Z*GJAC~0M*nWD}j@VmA-dZ2$lO literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_enable_blind_signing/00004.png b/tests/snapshots/nanos_enable_blind_signing/00004.png new file mode 100644 index 0000000000000000000000000000000000000000..7b3e0eabd1c82eea977be9fade67750f84753ce1 GIT binary patch literal 514 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfg9g%ah(kkI(#U9*Fz&w z#-|gd@=m;wnsC!p;d37Ei9MdoM)hkMXBtOVvKcuajx~z+d*+;SF72rfNQuk|8IwPO zhZQzwJEUDNaBgd3p1F{5X6)g{GZzY+p9zM|b3Cl@Sil0+c z<{dm`obkiN2yA6>IMC>oQfxEb1q1Zld4Ud5_?!r#mP$>i%zS9XE-4i!6ri_ViZ8{^ z7w9DW%MEArnNFqZ@t*jzpSRwqu_>D>l2K;8N9OiqpInYuB{ nwiV30btpB9d-_Ts=bL<{$o6E3u>LE+xMlEk^>bP0l+XkKgZ$Tj literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_enable_blind_signing/00005.png b/tests/snapshots/nanos_enable_blind_signing/00005.png new file mode 100644 index 0000000000000000000000000000000000000000..28e70e8bc22cd0c7f2b114c25504765b2c7b1592 GIT binary patch literal 614 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfi$Qx;TbZFupw)7<4;|!R4T_ z-r0ZRHu9h6L_ZYU7!dI>FmED*5n)}qyMo{vg+#N%Fy zE_W6T__ip~`B}ZG!e?En36)~p)ykVMHJowIba*DHV>7GGGc9XI$}%<(YHQ=2+1s{T zDl8>KLW)fiNPkOuIMGDG*ksKKAKnvty3br_IOE1_6nt34*p$ZzC~?weE)oe$5J zdY2^uRB0;|kOy?#6{!i4Ku2ksDipsyw3aQ!&)==BEt=_6H(Ltd;zZYHFM(>D9DXik zoOzkiMB(%4R&gVfIrCzejr8^Qs5u{g=?Rpd)ljsWai;#&hBICuXSSWWvO#h4#SMza zKsim`6MuqvfevI#$w7hziMua0 zoM|_$i8wq@M`kWaMf*t}-uFtI)y3WAX3HHRKeIp zVe>_xz=r9qwm{yA3kAC_IUJrB_U&51ua-0S!oH~)o6eb+ovP1!BIm?~1|B05AEOf& z3{EOIA6D6X@q;pN^O@- z;Qq|-uJ1;k!0eR%}dZ70wNT%?)Kbtt! z_9W1g!5|O%7&#wqJLAkNZ3OkJ!AT$9^BanoW?CBsGaHqsENgRYV@rXeHlRH#6ZN&L zCog22IUN`@+f8et4{JwSTFnFs$1yTmjiJ`OI>ueLkT0ny~PpB+Hb`Va!JVJ(-Qx=RGXR&|ZH3s=2{y4(DSd?*@viR&t z=bHLrdfFFAi;Jt?Wu3cYhlZrWfszN0{HK%%^;~0_p%)V~g@?l`u&B^b;YVM0vgXbm zFZ6z#V)g14u82gJ|cd1FeBo|&q-UYv%dYk`*P z?Cx&vm3N~G+n3Ypn6rVy$@M^GenN9j0ri|^1uDK7^MFa*zNnCkF>Q-z h&$XtDQ3g@f?CJZmIXzZhYz2lDgQu&X%Q~loCICmNg$4is literal 0 HcmV?d00001 diff --git a/tests/snapshots/nanos_enable_blind_signing/00008.png b/tests/snapshots/nanos_enable_blind_signing/00008.png new file mode 100644 index 0000000000000000000000000000000000000000..0bef4f3a7b4f6195c1d9fc70dcd11adb9d29bb52 GIT binary patch literal 531 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3HF6DHW(PFfiWoba4!+V0?S@KDXZ;sPq04($@Y}pt$UkYuJ7O_vMLEmi+4c;Fw95@=>aR7Nxv47?$ZTZtr_3n+(gx*XR_$~kWo+{1#Gb1SXD%3= zxUfOl`S7wtWoKq1=fesh37M0pZl2)bJ;7rH6tf4a>N|5`!^DM*Gq+1kkOZQXHnx<$ zq^B8D6K1Bx%Ze~iP)OohG zIi_9y5O&xt%^k>mro%ENocDx`AG1-use^*|uq^-bNuC4jCC(rnYh>Cs(MmB?|tDnm{ Hr-UW|v$x^l literal 0 HcmV?d00001 diff --git a/tests/src/approve.test.js b/tests/src/approve.test.js index fea24a5..63496f5 100644 --- a/tests/src/approve.test.js +++ b/tests/src/approve.test.js @@ -19,7 +19,7 @@ test('[Nano S] Approve DAI tokens', zemu("nanos", async (sim, eth) => { }); })); -test('[Nano X] Approve DAI tokens', zemu("nanox", async (sim, eth) => { +test.skip('[Nano X] Approve DAI tokens', zemu("nanox", async (sim, eth) => { const tx = eth.signTransaction( "44'/60'/1'/0/0", diff --git a/tests/src/blind_compound_deposit.test.js b/tests/src/blind_compound_deposit.test.js index 537ed8d..f38fe8e 100644 --- a/tests/src/blind_compound_deposit.test.js +++ b/tests/src/blind_compound_deposit.test.js @@ -3,6 +3,8 @@ import "regenerator-runtime/runtime"; import { waitForAppScreen, zemu } from './test.fixture'; test('[Nano S] Deposit ETH on compound, blind sign', zemu("nanos", async (sim, eth) => { + // Enable blind-signing + await sim.navigateAndCompareSnapshots('.', 'nanos_enable_blind_signing', [-2, 0, 0, 3, 0]); const tx = eth.signTransaction( "44'/60'/1'/0/0", @@ -19,7 +21,7 @@ test('[Nano S] Deposit ETH on compound, blind sign', zemu("nanos", async (sim, e }); })); -test('[Nano X] Deposit ETH on compound, blind sign', zemu("nanox", async (sim, eth) => { +test.skip('[Nano X] Deposit ETH on compound, blind sign', zemu("nanox", async (sim, eth) => { const tx = eth.signTransaction( "44'/60'/1'/0/0", diff --git a/tests/src/chainid.test.js b/tests/src/chainid.test.js index 7461c83..10247fc 100644 --- a/tests/src/chainid.test.js +++ b/tests/src/chainid.test.js @@ -36,7 +36,7 @@ test('[Nano S] Transfer on palm network on Ethereum', zemu("nanos", async (sim, }); })); -test('[Nano X] Transfer on network 112233445566 on Ethereum', zemu("nanox", async (sim, eth) => { +test.skip('[Nano X] Transfer on network 112233445566 on Ethereum', zemu("nanox", async (sim, eth) => { const tx = eth.signTransaction( "44'/60'/1'/0/0", @@ -53,7 +53,7 @@ test('[Nano X] Transfer on network 112233445566 on Ethereum', zemu("nanox", asyn }); })); -test('[Nano X] Transfer on palm network on Ethereum', zemu("nanox", async (sim, eth) => { +test.skip('[Nano X] Transfer on palm network on Ethereum', zemu("nanox", async (sim, eth) => { const tx = eth.signTransaction( "44'/60'/1'/0/0", @@ -68,4 +68,4 @@ test('[Nano X] Transfer on palm network on Ethereum', zemu("nanox", async (sim, "s": "3698e84564e58477a49f7a9cea572ef5d672a5538db08f3ee42df5eb75a1b907", "v": "0542b8613d", }); -})); \ No newline at end of file +})); diff --git a/tests/src/contract_data_warning.test.js b/tests/src/contract_data_warning.test.js index 2ebb16f..49fb200 100644 --- a/tests/src/contract_data_warning.test.js +++ b/tests/src/contract_data_warning.test.js @@ -6,9 +6,6 @@ import { waitForAppScreen, zemu } from './test.fixture'; import Zemu from '@zondax/zemu'; test('[Nano S] Try to blind sign with setting disabled', zemu("nanos", async (sim, eth) => { - // disable blind signing - await sim.navigateAndCompareSnapshots('.', 'nanos_disable_blind_signing', [-2, 0, 0, 3, 0]); - // we can't use eth.signTransaction because it detects that contract data is disabled and fails early let transport = await sim.getTransport(); let buffer = Buffer.from("058000002c8000003c800000010000000000000000f849208506fc23ac008303dc3194f650c3d88d12db855b8bf7d11be6c55a4e07dcc980a4a1712d6800000000000000000000000000000000000000000000000000000000000acbc7018080", "hex"); @@ -21,7 +18,7 @@ test('[Nano S] Try to blind sign with setting disabled', zemu("nanos", async (si await sim.navigateAndCompareSnapshots('.', 'nanos_try_to_blind_sign_with_setting_disabled', [1, 0]); })); -test('[Nano X] Try to blind sign with setting disabled', zemu("nanox", async (sim, eth) => { +test.skip('[Nano X] Try to blind sign with setting disabled', zemu("nanox", async (sim, eth) => { // disable blind signing await sim.navigateAndCompareSnapshots('.', 'nanox_disable_blind_signing', [-2, 0, 0, 3, 0]); @@ -35,4 +32,4 @@ test('[Nano X] Try to blind sign with setting disabled', zemu("nanox", async (si await Zemu.sleep(1000); await waitForAppScreen(sim); await sim.navigateAndCompareSnapshots('.', 'nanox_try_to_blind_sign_with_setting_disabled', [0]); -})); \ No newline at end of file +})); diff --git a/tests/src/eip1559.test.js b/tests/src/eip1559.test.js index 10831c6..37c8037 100644 --- a/tests/src/eip1559.test.js +++ b/tests/src/eip1559.test.js @@ -20,7 +20,7 @@ test('[Nano S] Transfer eip1559', zemu("nanos", async (sim, eth) => { })); -test('[Nano X] Transfer eip1559', zemu("nanox", async (sim, eth) => { +test.skip('[Nano X] Transfer eip1559', zemu("nanox", async (sim, eth) => { const tx = eth.signTransaction( "44'/60'/0'/0/0", @@ -36,4 +36,4 @@ test('[Nano X] Transfer eip1559', zemu("nanox", async (sim, eth) => { "v": "01" }); -})); \ No newline at end of file +})); diff --git a/tests/src/erc1155.test .js b/tests/src/erc1155.test.js similarity index 100% rename from tests/src/erc1155.test .js rename to tests/src/erc1155.test.js diff --git a/tests/src/erc721.test.js b/tests/src/erc721.test.js index 111d501..58052f1 100644 --- a/tests/src/erc721.test.js +++ b/tests/src/erc721.test.js @@ -2,7 +2,7 @@ import "core-js/stable"; import "regenerator-runtime/runtime"; import { waitForAppScreen, zemu, txFromEtherscan } from './test.fixture'; -test('[Nano S] Transfer erc721', zemu("nanos", async (sim, eth) => { +test.skip('[Nano S] Transfer erc721', zemu("nanos", async (sim, eth) => { // https://etherscan.io/tx/0x73cec4fc07de3a24ba42e8756e13b7ddfa9bd449126c37640881195e8ea9e679 // Modified to put a bigger token id @@ -24,7 +24,7 @@ test('[Nano S] Transfer erc721', zemu("nanos", async (sim, eth) => { }); })); -test('[Nano S] Transfer erc721 with attached ETH', zemu("nanos", async (sim, eth) => { +test.skip('[Nano S] Transfer erc721 with attached ETH', zemu("nanos", async (sim, eth) => { const rawTx = "0x02f8d601058459682f0085233da9943e8301865b94bd3531da5cf5857e7cfaa92426877b022e612cf8854242424242b86423b872dd0000000000000000000000004cc568b73c0dcf8e90db26d7fd3a6cfadca108a3000000000000000000000000d4c9b20950c3eca38fc1f33f54bdf9694e4887990000000000000000000000000000000000000000000000000000000000000f21c080a094c8632fe7277aa8c54cea9d81a15911cfa4970a2bf7356d14d04cc5afbcdab7a013a77b8c79e5d9b2b35edb3c44db3bb41b92f5c463ff126bf19d213b2b9ba8b5" const serializedTx = txFromEtherscan(rawTx); @@ -45,7 +45,7 @@ test('[Nano S] Transfer erc721 with attached ETH', zemu("nanos", async (sim, eth }); })); -test('[Nano S] set approval for all erc721', zemu("nanos", async (sim, eth) => { +test.skip('[Nano S] set approval for all erc721', zemu("nanos", async (sim, eth) => { // https://etherscan.io/tx/0x86b936db53c19fddf26b8d145f165e1c7fdff3c0f8b14b7758a38f0400cfd93f const rawTx = "0x02f8b0010c8459682f00852cfbb00ee682b54294d4e4078ca3495de5b1d4db434bebc5a98619778280b844a22cb4650000000000000000000000002efcb1e8d4472d35356b9747bea8a051eac2e3f50000000000000000000000000000000000000000000000000000000000000001c001a0c5b8c024c15ca1452ce8a13eacfcdc25f1c6f581bb3ce570e82f08f1b792b3aca03be4dba0302ae190618a72eb1202ce3af3e17afd7d8a94345a48cae5cad15541"; @@ -87,7 +87,7 @@ test.skip('[Nano S] approval erc721', zemu("nanos", async (sim, eth) => { }); })); -test('[Nano S] safe transfer erc721', zemu("nanos", async (sim, eth) => { +test.skip('[Nano S] safe transfer erc721', zemu("nanos", async (sim, eth) => { // https://etherscan.io/tx/0x1ee6ce9be1c9fe6f030ff124ba8c88a410223c022816547e4b3fedd3a4d2dc1e const rawTx = "0xf8cc82028585077359400083061a8094d4e4078ca3495de5b1d4db434bebc5a98619778280b86442842e0e000000000000000000000000c352b534e8b987e036a93539fd6897f53488e56a0000000000000000000000000a9287d9339c175cd3ea0ad4228f734a9f75ee6200000000000000000000000000000000000000000000000000000000000000621ca08250f4b2c8f28c5e4ef621dba4682990d1faf930c8cb6d032c6e7278e8951d92a03c1e1f6d63ed339041f69f24c6c0968ba26f244f779cb4fa7a468f3ba3d3e06e"; @@ -127,4 +127,4 @@ test.skip('[Nano S] safe transfer with data erc721', zemu("nanos", async (sim, e await expect(tx).resolves.toEqual({ }); -})); \ No newline at end of file +})); diff --git a/tests/src/send.test.js b/tests/src/send.test.js index cb82ef4..be27c78 100644 --- a/tests/src/send.test.js +++ b/tests/src/send.test.js @@ -47,7 +47,7 @@ test('[Nano S] Transfer Ether on network 5234 on Ethereum app', zemu("nanos", as }); })); -test('[Nano X] Transfer Ether on Ethereum app', zemu("nanox", async (sim, eth) => { +test.skip('[Nano X] Transfer Ether on Ethereum app', zemu("nanox", async (sim, eth) => { const tx = eth.signTransaction( "44'/60'/1'/0/0", @@ -64,7 +64,7 @@ test('[Nano X] Transfer Ether on Ethereum app', zemu("nanox", async (sim, eth) = }); })); -test('[Nano X] Transfer Ether on network 5234 on Ethereum app', zemu("nanox", async (sim, eth) => { +test.skip('[Nano X] Transfer Ether on network 5234 on Ethereum app', zemu("nanox", async (sim, eth) => { const tx = eth.signTransaction( "44'/60'/1'/0/0", diff --git a/tests/src/send_bsc.test.js b/tests/src/send_bsc.test.js index 4e1bfa9..02d5112 100644 --- a/tests/src/send_bsc.test.js +++ b/tests/src/send_bsc.test.js @@ -19,7 +19,7 @@ test('[Nano S] Transfer bsc', zemu("nanos", async (sim, eth) => { }); })); -test('[Nano X] Transfer bsc', zemu("nanox", async (sim, eth) => { +test.skip('[Nano X] Transfer bsc', zemu("nanox", async (sim, eth) => { const tx = eth.signTransaction( "44'/60'/1'/0/0", @@ -34,4 +34,4 @@ test('[Nano X] Transfer bsc', zemu("nanox", async (sim, eth) => { "s": "6b35492b7108d9d9e1cc7aede536ed6b3173197b56dd873cbc3b43e041d6f407", "v": "93", }); -})); \ No newline at end of file +})); diff --git a/tests/src/send_etc.test.js b/tests/src/send_etc.test.js index 5a2d76c..c1cbaa5 100644 --- a/tests/src/send_etc.test.js +++ b/tests/src/send_etc.test.js @@ -60,7 +60,7 @@ test("[Nano S] Transfer on network 5234 on Ethereum clone", async () => { } }); -test("[Nano X] Transfer on Ethereum clone app", async () => { +test.skip("[Nano X] Transfer on Ethereum clone app", async () => { jest.setTimeout(TIMEOUT); const sim = new Zemu(NANOX_CLONE_ELF_PATH, NANOX_ETH_LIB); @@ -89,7 +89,7 @@ test("[Nano X] Transfer on Ethereum clone app", async () => { } }); -test("[Nano X] Transfer on network 5234 on Ethereum clone", async () => { +test.skip("[Nano X] Transfer on network 5234 on Ethereum clone", async () => { jest.setTimeout(TIMEOUT); const sim = new Zemu(NANOX_CLONE_ELF_PATH, NANOX_ETH_LIB); @@ -111,4 +111,4 @@ test("[Nano X] Transfer on network 5234 on Ethereum clone", async () => { } finally { await sim.close(); } -}); \ No newline at end of file +}); diff --git a/tests/src/starkware.test.js b/tests/src/starkware.test.js index 84bb16e..b4dd707 100644 --- a/tests/src/starkware.test.js +++ b/tests/src/starkware.test.js @@ -34,7 +34,7 @@ test('[Nano S] Transfer Ether on Ethereum app', zemu("nanos", async (sim, eth) = }); })); -test('[Nano X] Transfer Ether on Ethereum app', zemu("nanox", async (sim, eth) => { +test.skip('[Nano X] Transfer Ether on Ethereum app', zemu("nanox", async (sim, eth) => { // Provide USDT token info to the app const usdt_info = byContractAddressAndChainId("0xdac17f958d2ee523a2206206994597c13d831ec7", 1); diff --git a/tests/yarn.lock b/tests/yarn.lock index f40fad1..95fdaaf 100644 --- a/tests/yarn.lock +++ b/tests/yarn.lock @@ -966,6 +966,21 @@ "@ethersproject/properties" "^5.4.0" "@ethersproject/strings" "^5.4.0" +"@ethersproject/abi@5.5.0", "@ethersproject/abi@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.5.0.tgz#fb52820e22e50b854ff15ce1647cc508d6660613" + integrity sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w== + dependencies: + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/abstract-provider@5.4.1", "@ethersproject/abstract-provider@^5.4.0": version "5.4.1" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.4.1.tgz#e404309a29f771bd4d28dbafadcaa184668c2a6e" @@ -979,6 +994,19 @@ "@ethersproject/transactions" "^5.4.0" "@ethersproject/web" "^5.4.0" +"@ethersproject/abstract-provider@5.5.1", "@ethersproject/abstract-provider@^5.5.0": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz#2f1f6e8a3ab7d378d8ad0b5718460f85649710c5" + integrity sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/networks" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/web" "^5.5.0" + "@ethersproject/abstract-signer@5.4.1", "@ethersproject/abstract-signer@^5.4.0": version "5.4.1" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.4.1.tgz#e4e9abcf4dd4f1ba0db7dff9746a5f78f355ea81" @@ -990,6 +1018,17 @@ "@ethersproject/logger" "^5.4.0" "@ethersproject/properties" "^5.4.0" +"@ethersproject/abstract-signer@5.5.0", "@ethersproject/abstract-signer@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz#590ff6693370c60ae376bf1c7ada59eb2a8dd08d" + integrity sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA== + dependencies: + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/address@5.4.0", "@ethersproject/address@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.4.0.tgz#ba2d00a0f8c4c0854933b963b9a3a9f6eb4a37a3" @@ -1001,6 +1040,17 @@ "@ethersproject/logger" "^5.4.0" "@ethersproject/rlp" "^5.4.0" +"@ethersproject/address@5.5.0", "@ethersproject/address@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.5.0.tgz#bcc6f576a553f21f3dd7ba17248f81b473c9c78f" + integrity sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + "@ethersproject/base64@5.4.0", "@ethersproject/base64@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.4.0.tgz#7252bf65295954c9048c7ca5f43e5c86441b2a9a" @@ -1008,6 +1058,13 @@ dependencies: "@ethersproject/bytes" "^5.4.0" +"@ethersproject/base64@5.5.0", "@ethersproject/base64@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.5.0.tgz#881e8544e47ed976930836986e5eb8fab259c090" + integrity sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/basex@5.4.0", "@ethersproject/basex@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.4.0.tgz#0a2da0f4e76c504a94f2b21d3161ed9438c7f8a6" @@ -1016,6 +1073,14 @@ "@ethersproject/bytes" "^5.4.0" "@ethersproject/properties" "^5.4.0" +"@ethersproject/basex@5.5.0", "@ethersproject/basex@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.5.0.tgz#e40a53ae6d6b09ab4d977bd037010d4bed21b4d3" + integrity sha512-ZIodwhHpVJ0Y3hUCfUucmxKsWQA5TMnavp5j/UOuDdzZWzJlRmuOjcTMIGgHCYuZmHt36BfiSyQPSRskPxbfaQ== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/bignumber@5.4.1", "@ethersproject/bignumber@^5.4.0": version "5.4.1" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.4.1.tgz#64399d3b9ae80aa83d483e550ba57ea062c1042d" @@ -1025,6 +1090,15 @@ "@ethersproject/logger" "^5.4.0" bn.js "^4.11.9" +"@ethersproject/bignumber@5.5.0", "@ethersproject/bignumber@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.5.0.tgz#875b143f04a216f4f8b96245bde942d42d279527" + integrity sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + bn.js "^4.11.9" + "@ethersproject/bytes@5.4.0", "@ethersproject/bytes@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.4.0.tgz#56fa32ce3bf67153756dbaefda921d1d4774404e" @@ -1032,6 +1106,13 @@ dependencies: "@ethersproject/logger" "^5.4.0" +"@ethersproject/bytes@5.5.0", "@ethersproject/bytes@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" + integrity sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog== + dependencies: + "@ethersproject/logger" "^5.5.0" + "@ethersproject/constants@5.4.0", "@ethersproject/constants@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.4.0.tgz#ee0bdcb30bf1b532d2353c977bf2ef1ee117958a" @@ -1039,6 +1120,13 @@ dependencies: "@ethersproject/bignumber" "^5.4.0" +"@ethersproject/constants@5.5.0", "@ethersproject/constants@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.5.0.tgz#d2a2cd7d94bd1d58377d1d66c4f53c9be4d0a45e" + integrity sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/contracts@5.4.1": version "5.4.1" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.4.1.tgz#3eb4f35b7fe60a962a75804ada2746494df3e470" @@ -1055,6 +1143,22 @@ "@ethersproject/properties" "^5.4.0" "@ethersproject/transactions" "^5.4.0" +"@ethersproject/contracts@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.5.0.tgz#b735260d4bd61283a670a82d5275e2a38892c197" + integrity sha512-2viY7NzyvJkh+Ug17v7g3/IJC8HqZBDcOjYARZLdzRxrfGlRgmYgl6xPRKVbEzy1dWKw/iv7chDcS83pg6cLxg== + dependencies: + "@ethersproject/abi" "^5.5.0" + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/hash@5.4.0", "@ethersproject/hash@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.4.0.tgz#d18a8e927e828e22860a011f39e429d388344ae0" @@ -1069,6 +1173,20 @@ "@ethersproject/properties" "^5.4.0" "@ethersproject/strings" "^5.4.0" +"@ethersproject/hash@5.5.0", "@ethersproject/hash@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.5.0.tgz#7cee76d08f88d1873574c849e0207dcb32380cc9" + integrity sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg== + dependencies: + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/hdnode@5.4.0", "@ethersproject/hdnode@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.4.0.tgz#4bc9999b9a12eb5ce80c5faa83114a57e4107cac" @@ -1087,6 +1205,24 @@ "@ethersproject/transactions" "^5.4.0" "@ethersproject/wordlists" "^5.4.0" +"@ethersproject/hdnode@5.5.0", "@ethersproject/hdnode@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.5.0.tgz#4a04e28f41c546f7c978528ea1575206a200ddf6" + integrity sha512-mcSOo9zeUg1L0CoJH7zmxwUG5ggQHU1UrRf8jyTYy6HxdZV+r0PBoL1bxr+JHIPXRzS6u/UW4mEn43y0tmyF8Q== + dependencies: + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/basex" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/pbkdf2" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + "@ethersproject/signing-key" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/wordlists" "^5.5.0" + "@ethersproject/json-wallets@5.4.0", "@ethersproject/json-wallets@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.4.0.tgz#2583341cfe313fc9856642e8ace3080154145e95" @@ -1106,6 +1242,25 @@ aes-js "3.0.0" scrypt-js "3.0.1" +"@ethersproject/json-wallets@5.5.0", "@ethersproject/json-wallets@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.5.0.tgz#dd522d4297e15bccc8e1427d247ec8376b60e325" + integrity sha512-9lA21XQnCdcS72xlBn1jfQdj2A1VUxZzOzi9UkNdnokNKke/9Ya2xA9aIK1SC3PQyBDLt4C+dfps7ULpkvKikQ== + dependencies: + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/hdnode" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/pbkdf2" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/random" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + aes-js "3.0.0" + scrypt-js "3.0.1" + "@ethersproject/keccak256@5.4.0", "@ethersproject/keccak256@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.4.0.tgz#7143b8eea4976080241d2bd92e3b1f1bf7025318" @@ -1114,11 +1269,24 @@ "@ethersproject/bytes" "^5.4.0" js-sha3 "0.5.7" +"@ethersproject/keccak256@5.5.0", "@ethersproject/keccak256@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.5.0.tgz#e4b1f9d7701da87c564ffe336f86dcee82983492" + integrity sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg== + dependencies: + "@ethersproject/bytes" "^5.5.0" + js-sha3 "0.8.0" + "@ethersproject/logger@5.4.0", "@ethersproject/logger@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.4.0.tgz#f39adadf62ad610c420bcd156fd41270e91b3ca9" integrity sha512-xYdWGGQ9P2cxBayt64d8LC8aPFJk6yWCawQi/4eJ4+oJdMMjEBMrIcIMZ9AxhwpPVmnBPrsB10PcXGmGAqgUEQ== +"@ethersproject/logger@5.5.0", "@ethersproject/logger@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" + integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== + "@ethersproject/networks@5.4.2", "@ethersproject/networks@^5.4.0": version "5.4.2" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.4.2.tgz#2247d977626e97e2c3b8ee73cd2457babde0ce35" @@ -1126,6 +1294,13 @@ dependencies: "@ethersproject/logger" "^5.4.0" +"@ethersproject/networks@5.5.0", "@ethersproject/networks@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.5.0.tgz#babec47cab892c51f8dd652ce7f2e3e14283981a" + integrity sha512-KWfP3xOnJeF89Uf/FCJdV1a2aDJe5XTN2N52p4fcQ34QhDqQFkgQKZ39VGtiqUgHcLI8DfT0l9azC3KFTunqtA== + dependencies: + "@ethersproject/logger" "^5.5.0" + "@ethersproject/pbkdf2@5.4.0", "@ethersproject/pbkdf2@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.4.0.tgz#ed88782a67fda1594c22d60d0ca911a9d669641c" @@ -1134,6 +1309,14 @@ "@ethersproject/bytes" "^5.4.0" "@ethersproject/sha2" "^5.4.0" +"@ethersproject/pbkdf2@5.5.0", "@ethersproject/pbkdf2@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz#e25032cdf02f31505d47afbf9c3e000d95c4a050" + integrity sha512-SaDvQFvXPnz1QGpzr6/HToLifftSXGoXrbpZ6BvoZhmx4bNLHrxDe8MZisuecyOziP1aVEwzC2Hasj+86TgWVg== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + "@ethersproject/properties@5.4.0", "@ethersproject/properties@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.4.0.tgz#38ba20539b44dcc5d5f80c45ad902017dcdbefe7" @@ -1141,6 +1324,13 @@ dependencies: "@ethersproject/logger" "^5.4.0" +"@ethersproject/properties@5.5.0", "@ethersproject/properties@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.5.0.tgz#61f00f2bb83376d2071baab02245f92070c59995" + integrity sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA== + dependencies: + "@ethersproject/logger" "^5.5.0" + "@ethersproject/providers@5.4.4": version "5.4.4" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.4.4.tgz#6729120317942fc0ab0ecdb35e944ec6bbedb795" @@ -1166,6 +1356,31 @@ bech32 "1.1.4" ws "7.4.6" +"@ethersproject/providers@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.5.0.tgz#bc2876a8fe5e0053ed9828b1f3767ae46e43758b" + integrity sha512-xqMbDnS/FPy+J/9mBLKddzyLLAQFjrVff5g00efqxPzcAwXiR+SiCGVy6eJ5iAIirBOATjx7QLhDNPGV+AEQsw== + dependencies: + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/basex" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/networks" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/random" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/web" "^5.5.0" + bech32 "1.1.4" + ws "7.4.6" + "@ethersproject/random@5.4.0", "@ethersproject/random@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.4.0.tgz#9cdde60e160d024be39cc16f8de3b9ce39191e16" @@ -1174,6 +1389,14 @@ "@ethersproject/bytes" "^5.4.0" "@ethersproject/logger" "^5.4.0" +"@ethersproject/random@5.5.0", "@ethersproject/random@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.5.0.tgz#305ed9e033ca537735365ac12eed88580b0f81f9" + integrity sha512-egGYZwZ/YIFKMHcoBUo8t3a8Hb/TKYX8BCBoLjudVCZh892welR3jOxgOmb48xznc9bTcMm7Tpwc1gHC1PFNFQ== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/rlp@5.4.0", "@ethersproject/rlp@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.4.0.tgz#de61afda5ff979454e76d3b3310a6c32ad060931" @@ -1182,6 +1405,14 @@ "@ethersproject/bytes" "^5.4.0" "@ethersproject/logger" "^5.4.0" +"@ethersproject/rlp@5.5.0", "@ethersproject/rlp@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.5.0.tgz#530f4f608f9ca9d4f89c24ab95db58ab56ab99a0" + integrity sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/sha2@5.4.0", "@ethersproject/sha2@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.4.0.tgz#c9a8db1037014cbc4e9482bd662f86c090440371" @@ -1191,6 +1422,15 @@ "@ethersproject/logger" "^5.4.0" hash.js "1.1.7" +"@ethersproject/sha2@5.5.0", "@ethersproject/sha2@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.5.0.tgz#a40a054c61f98fd9eee99af2c3cc6ff57ec24db7" + integrity sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + hash.js "1.1.7" + "@ethersproject/signing-key@5.4.0", "@ethersproject/signing-key@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.4.0.tgz#2f05120984e81cf89a3d5f6dec5c68ee0894fbec" @@ -1203,6 +1443,18 @@ elliptic "6.5.4" hash.js "1.1.7" +"@ethersproject/signing-key@5.5.0", "@ethersproject/signing-key@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.5.0.tgz#2aa37169ce7e01e3e80f2c14325f624c29cedbe0" + integrity sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + bn.js "^4.11.9" + elliptic "6.5.4" + hash.js "1.1.7" + "@ethersproject/solidity@5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.4.0.tgz#1305e058ea02dc4891df18b33232b11a14ece9ec" @@ -1214,6 +1466,18 @@ "@ethersproject/sha2" "^5.4.0" "@ethersproject/strings" "^5.4.0" +"@ethersproject/solidity@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.5.0.tgz#2662eb3e5da471b85a20531e420054278362f93f" + integrity sha512-9NgZs9LhGMj6aCtHXhtmFQ4AN4sth5HuFXVvAQtzmm0jpSCNOTGtrHZJAeYTh7MBjRR8brylWZxBZR9zDStXbw== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/strings@5.4.0", "@ethersproject/strings@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.4.0.tgz#fb12270132dd84b02906a8d895ae7e7fa3d07d9a" @@ -1223,6 +1487,15 @@ "@ethersproject/constants" "^5.4.0" "@ethersproject/logger" "^5.4.0" +"@ethersproject/strings@5.5.0", "@ethersproject/strings@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.5.0.tgz#e6784d00ec6c57710755699003bc747e98c5d549" + integrity sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/transactions@5.4.0", "@ethersproject/transactions@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.4.0.tgz#a159d035179334bd92f340ce0f77e83e9e1522e0" @@ -1238,6 +1511,21 @@ "@ethersproject/rlp" "^5.4.0" "@ethersproject/signing-key" "^5.4.0" +"@ethersproject/transactions@5.5.0", "@ethersproject/transactions@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.5.0.tgz#7e9bf72e97bcdf69db34fe0d59e2f4203c7a2908" + integrity sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA== + dependencies: + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + "@ethersproject/signing-key" "^5.5.0" + "@ethersproject/units@5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.4.0.tgz#d57477a4498b14b88b10396062c8cbbaf20c79fe" @@ -1247,6 +1535,15 @@ "@ethersproject/constants" "^5.4.0" "@ethersproject/logger" "^5.4.0" +"@ethersproject/units@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.5.0.tgz#104d02db5b5dc42cc672cc4587bafb87a95ee45e" + integrity sha512-7+DpjiZk4v6wrikj+TCyWWa9dXLNU73tSTa7n0TSJDxkYbV3Yf1eRh9ToMLlZtuctNYu9RDNNy2USq3AdqSbag== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/wallet@5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.4.0.tgz#fa5b59830b42e9be56eadd45a16a2e0933ad9353" @@ -1268,6 +1565,27 @@ "@ethersproject/transactions" "^5.4.0" "@ethersproject/wordlists" "^5.4.0" +"@ethersproject/wallet@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.5.0.tgz#322a10527a440ece593980dca6182f17d54eae75" + integrity sha512-Mlu13hIctSYaZmUOo7r2PhNSd8eaMPVXe1wxrz4w4FCE4tDYBywDH+bAR1Xz2ADyXGwqYMwstzTrtUVIsKDO0Q== + dependencies: + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/hdnode" "^5.5.0" + "@ethersproject/json-wallets" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/random" "^5.5.0" + "@ethersproject/signing-key" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/wordlists" "^5.5.0" + "@ethersproject/web@5.4.0", "@ethersproject/web@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.4.0.tgz#49fac173b96992334ed36a175538ba07a7413d1f" @@ -1279,6 +1597,17 @@ "@ethersproject/properties" "^5.4.0" "@ethersproject/strings" "^5.4.0" +"@ethersproject/web@5.5.0", "@ethersproject/web@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.5.0.tgz#0e5bb21a2b58fb4960a705bfc6522a6acf461e28" + integrity sha512-BEgY0eL5oH4mAo37TNYVrFeHsIXLRxggCRG/ksRIxI2X5uj5IsjGmcNiRN/VirQOlBxcUhCgHhaDLG4m6XAVoA== + dependencies: + "@ethersproject/base64" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/wordlists@5.4.0", "@ethersproject/wordlists@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.4.0.tgz#f34205ec3bbc9e2c49cadaee774cf0b07e7573d7" @@ -1290,6 +1619,17 @@ "@ethersproject/properties" "^5.4.0" "@ethersproject/strings" "^5.4.0" +"@ethersproject/wordlists@5.5.0", "@ethersproject/wordlists@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.5.0.tgz#aac74963aa43e643638e5172353d931b347d584f" + integrity sha512-bL0UTReWDiaQJJYOC9sh/XcRu/9i2jMrzf8VLRmPKx58ckSlOJiohODkECCO50dtLZHcGU6MLXQ4OOrgBwP77Q== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@grpc/grpc-js@^1.3.4": version "1.3.7" resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.3.7.tgz#58b687aff93b743aafde237fd2ee9a3259d7f2d8" @@ -3176,6 +3516,42 @@ ethers@^5.4.4: "@ethersproject/web" "5.4.0" "@ethersproject/wordlists" "5.4.0" +ethers@^5.5.1: + version "5.5.1" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.5.1.tgz#d3259a95a42557844aa543906c537106c0406fbf" + integrity sha512-RodEvUFZI+EmFcE6bwkuJqpCYHazdzeR1nMzg+YWQSmQEsNtfl1KHGfp/FWZYl48bI/g7cgBeP2IlPthjiVngw== + dependencies: + "@ethersproject/abi" "5.5.0" + "@ethersproject/abstract-provider" "5.5.1" + "@ethersproject/abstract-signer" "5.5.0" + "@ethersproject/address" "5.5.0" + "@ethersproject/base64" "5.5.0" + "@ethersproject/basex" "5.5.0" + "@ethersproject/bignumber" "5.5.0" + "@ethersproject/bytes" "5.5.0" + "@ethersproject/constants" "5.5.0" + "@ethersproject/contracts" "5.5.0" + "@ethersproject/hash" "5.5.0" + "@ethersproject/hdnode" "5.5.0" + "@ethersproject/json-wallets" "5.5.0" + "@ethersproject/keccak256" "5.5.0" + "@ethersproject/logger" "5.5.0" + "@ethersproject/networks" "5.5.0" + "@ethersproject/pbkdf2" "5.5.0" + "@ethersproject/properties" "5.5.0" + "@ethersproject/providers" "5.5.0" + "@ethersproject/random" "5.5.0" + "@ethersproject/rlp" "5.5.0" + "@ethersproject/sha2" "5.5.0" + "@ethersproject/signing-key" "5.5.0" + "@ethersproject/solidity" "5.5.0" + "@ethersproject/strings" "5.5.0" + "@ethersproject/transactions" "5.5.0" + "@ethersproject/units" "5.5.0" + "@ethersproject/wallet" "5.5.0" + "@ethersproject/web" "5.5.0" + "@ethersproject/wordlists" "5.5.0" + events@^3.0.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -4806,6 +5182,11 @@ js-sha3@0.5.7: resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc= +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" From eb531de5d220f84177543bbdc0b91cff8011e0b6 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 14 Jan 2022 17:32:17 +0100 Subject: [PATCH 7/9] [clean] Linting pass --- src/eth_plugin_handler.c | 4 +- src/main.c | 2 +- src/shared_context.h | 8 ++-- .../cmd_provideNFTInfo.c | 28 +++++------ src_features/setPlugin/cmd_setPlugin.c | 22 ++++----- src_plugins/erc1155/erc1155_plugin.c | 2 +- src_plugins/erc1155/erc1155_plugin.h | 8 ++-- .../erc1155/erc1155_provide_parameters.c | 47 ++++++++----------- src_plugins/erc1155/erc1155_ui.c | 8 ++-- src_plugins/erc721/erc721_plugin.c | 2 +- src_plugins/erc721/erc721_plugin.h | 2 +- .../erc721/erc721_provide_parameters.c | 2 +- src_plugins/erc721/erc721_ui.c | 4 +- 13 files changed, 65 insertions(+), 74 deletions(-) diff --git a/src/eth_plugin_handler.c b/src/eth_plugin_handler.c index a6e685e..b82c6a5 100644 --- a/src/eth_plugin_handler.c +++ b/src/eth_plugin_handler.c @@ -133,7 +133,7 @@ eth_plugin_result_t eth_plugin_perform_init(uint8_t *contractAddress, #ifdef HAVE_NFT_SUPPORT case ERC1155: case ERC721: -#endif // HAVE_NFT_SUPPORT +#endif // HAVE_NFT_SUPPORT case EXTERNAL: eth_plugin_perform_init_default(contractAddress, init); contractAddress = NULL; @@ -277,7 +277,7 @@ eth_plugin_result_t eth_plugin_call(int method, void *parameter) { erc1155_plugin_call(method, parameter); break; } -#endif // HAVE_NFT_SUPPORT +#endif // HAVE_NFT_SUPPORT case OLD_INTERNAL: { // Perform the call for (i = 0;; i++) { diff --git a/src/main.c b/src/main.c index 7799632..a06bac0 100644 --- a/src/main.c +++ b/src/main.c @@ -528,7 +528,7 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { flags, tx); break; -#endif // HAVE_NFT_SUPPORT +#endif // HAVE_NFT_SUPPORT case INS_SET_EXTERNAL_PLUGIN: handleSetExternalPlugin(G_io_apdu_buffer[OFFSET_P1], diff --git a/src/shared_context.h b/src/shared_context.h index b5c944f..035f7c2 100644 --- a/src/shared_context.h +++ b/src/shared_context.h @@ -204,10 +204,10 @@ extern const internalStorage_t N_storage_real; extern bool called_from_swap; 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. + 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. } pluginType_t; extern pluginType_t pluginType; diff --git a/src_features/provideNFTInformation/cmd_provideNFTInfo.c b/src_features/provideNFTInformation/cmd_provideNFTInfo.c index 1495f2c..a4256a3 100644 --- a/src_features/provideNFTInformation/cmd_provideNFTInfo.c +++ b/src_features/provideNFTInformation/cmd_provideNFTInfo.c @@ -18,8 +18,8 @@ #define MIN_DER_SIG_SIZE 67 #define MAX_DER_SIG_SIZE 72 -#define TEST_NFT_METADATA_KEY 0 -#define PROD_NFT_METADATA_KEY 1 +#define TEST_NFT_METADATA_KEY 0 +#define PROD_NFT_METADATA_KEY 1 #define ALGORITHM_ID_1 1 @@ -29,18 +29,18 @@ static const uint8_t LEDGER_NFT_METADATA_PUBLIC_KEY[] = { #ifdef HAVE_NFT_TESTING_KEY - 0x04, 0xf5, 0x70, 0x0c, 0xa1, 0xe8, 0x74, 0x24, 0xc7, 0xc7, 0xd1, 0x19, 0xe7, - 0xe3, 0xc1, 0x89, 0xb1, 0x62, 0x50, 0x94, 0xdb, 0x6e, 0xa0, 0x40, 0x87, 0xc8, - 0x30, 0x00, 0x7d, 0x0b, 0x46, 0x9a, 0x53, 0x11, 0xee, 0x6a, 0x1a, 0xcd, 0x1d, - 0xa5, 0xaa, 0xb0, 0xf5, 0xc6, 0xdf, 0x13, 0x15, 0x8d, 0x28, 0xcc, 0x12, 0xd1, - 0xdd, 0xa6, 0xec, 0xe9, 0x46, 0xb8, 0x9d, 0x5c, 0x05, 0x49, 0x92, 0x59, 0xc4 + 0x04, 0xf5, 0x70, 0x0c, 0xa1, 0xe8, 0x74, 0x24, 0xc7, 0xc7, 0xd1, 0x19, 0xe7, 0xe3, + 0xc1, 0x89, 0xb1, 0x62, 0x50, 0x94, 0xdb, 0x6e, 0xa0, 0x40, 0x87, 0xc8, 0x30, 0x00, + 0x7d, 0x0b, 0x46, 0x9a, 0x53, 0x11, 0xee, 0x6a, 0x1a, 0xcd, 0x1d, 0xa5, 0xaa, 0xb0, + 0xf5, 0xc6, 0xdf, 0x13, 0x15, 0x8d, 0x28, 0xcc, 0x12, 0xd1, 0xdd, 0xa6, 0xec, 0xe9, + 0x46, 0xb8, 0x9d, 0x5c, 0x05, 0x49, 0x92, 0x59, 0xc4 #else - 0x04, 0x98, 0x8d, 0xa6, 0xb2, 0x46, 0xf2, 0x8e, 0x77, 0xc1, 0xba, 0xb6, 0x75, - 0xcb, 0x2a, 0x27, 0x44, 0xf7, 0xf5, 0xce, 0xc5, 0x6a, 0xe6, 0xe0, 0x32, 0x23, - 0x33, 0x7b, 0x57, 0x94, 0xcd, 0x6a, 0xe0, 0x7d, 0x48, 0xb3, 0x0d, 0xb9, 0xcc, - 0xb4, 0x0f, 0x5a, 0x02, 0xa1, 0x1a, 0x3a, 0xb9, 0x9d, 0x5f, 0x59, 0x5a, 0x3d, - 0x50, 0xa0, 0xe1, 0x30, 0x23, 0xfd, 0x0d, 0x95, 0x87, 0x92, 0xd7, 0x97, 0x01 + 0x04, 0x98, 0x8d, 0xa6, 0xb2, 0x46, 0xf2, 0x8e, 0x77, 0xc1, 0xba, 0xb6, 0x75, 0xcb, + 0x2a, 0x27, 0x44, 0xf7, 0xf5, 0xce, 0xc5, 0x6a, 0xe6, 0xe0, 0x32, 0x23, 0x33, 0x7b, + 0x57, 0x94, 0xcd, 0x6a, 0xe0, 0x7d, 0x48, 0xb3, 0x0d, 0xb9, 0xcc, 0xb4, 0x0f, 0x5a, + 0x02, 0xa1, 0x1a, 0x3a, 0xb9, 0x9d, 0x5f, 0x59, 0x5a, 0x3d, 0x50, 0xa0, 0xe1, 0x30, + 0x23, 0xfd, 0x0d, 0x95, 0x87, 0x92, 0xd7, 0x97, 0x01 #endif }; @@ -176,7 +176,7 @@ void handleProvideNFTInformation(uint8_t p1, switch (algorithmId) { case ALGORITHM_ID_1: curve = CX_CURVE_256K1; - verificationFn = (verificationAlgo*)cx_ecdsa_verify; + verificationFn = (verificationAlgo *) cx_ecdsa_verify; hashId = CX_SHA256; break; default: @@ -227,4 +227,4 @@ void handleProvideNFTInformation(uint8_t p1, THROW(0x9000); } -#endif // HAVE_NFT_SUPPORT +#endif // HAVE_NFT_SUPPORT diff --git a/src_features/setPlugin/cmd_setPlugin.c b/src_features/setPlugin/cmd_setPlugin.c index ea39aa2..d2969f7 100644 --- a/src_features/setPlugin/cmd_setPlugin.c +++ b/src_features/setPlugin/cmd_setPlugin.c @@ -46,17 +46,17 @@ typedef enum AlgorithmID { // Only used for signing NFT plugins (ERC721 and ERC1155) static const uint8_t LEDGER_NFT_SELECTOR_PUBLIC_KEY[] = { #ifdef HAVE_NFT_TESTING_KEY - 0x04, 0xf5, 0x70, 0x0c, 0xa1, 0xe8, 0x74, 0x24, 0xc7, 0xc7, 0xd1, 0x19, 0xe7, - 0xe3, 0xc1, 0x89, 0xb1, 0x62, 0x50, 0x94, 0xdb, 0x6e, 0xa0, 0x40, 0x87, 0xc8, - 0x30, 0x00, 0x7d, 0x0b, 0x46, 0x9a, 0x53, 0x11, 0xee, 0x6a, 0x1a, 0xcd, 0x1d, - 0xa5, 0xaa, 0xb0, 0xf5, 0xc6, 0xdf, 0x13, 0x15, 0x8d, 0x28, 0xcc, 0x12, 0xd1, - 0xdd, 0xa6, 0xec, 0xe9, 0x46, 0xb8, 0x9d, 0x5c, 0x05, 0x49, 0x92, 0x59, 0xc4 + 0x04, 0xf5, 0x70, 0x0c, 0xa1, 0xe8, 0x74, 0x24, 0xc7, 0xc7, 0xd1, 0x19, 0xe7, 0xe3, + 0xc1, 0x89, 0xb1, 0x62, 0x50, 0x94, 0xdb, 0x6e, 0xa0, 0x40, 0x87, 0xc8, 0x30, 0x00, + 0x7d, 0x0b, 0x46, 0x9a, 0x53, 0x11, 0xee, 0x6a, 0x1a, 0xcd, 0x1d, 0xa5, 0xaa, 0xb0, + 0xf5, 0xc6, 0xdf, 0x13, 0x15, 0x8d, 0x28, 0xcc, 0x12, 0xd1, 0xdd, 0xa6, 0xec, 0xe9, + 0x46, 0xb8, 0x9d, 0x5c, 0x05, 0x49, 0x92, 0x59, 0xc4 #else - 0x04, 0xd8, 0x62, 0x6e, 0x01, 0x9e, 0x55, 0x3e, 0x19, 0x69, 0x56, 0xf1, 0x17, - 0x4d, 0xcd, 0xb8, 0x9a, 0x1c, 0xda, 0xc4, 0x93, 0x90, 0x08, 0xbc, 0x79, 0x77, - 0x33, 0x6d, 0x78, 0x24, 0xee, 0xe3, 0xa2, 0x62, 0x24, 0x1a, 0x62, 0x73, 0x52, - 0x3b, 0x09, 0xb8, 0xd0, 0xce, 0x0d, 0x39, 0xe8, 0x60, 0xc9, 0x4d, 0x02, 0x53, - 0x58, 0xdb, 0xdc, 0x25, 0x92, 0xc7, 0xc6, 0x48, 0x0d, 0x39, 0xce, 0xbb, 0xa3 + 0x04, 0xd8, 0x62, 0x6e, 0x01, 0x9e, 0x55, 0x3e, 0x19, 0x69, 0x56, 0xf1, 0x17, 0x4d, + 0xcd, 0xb8, 0x9a, 0x1c, 0xda, 0xc4, 0x93, 0x90, 0x08, 0xbc, 0x79, 0x77, 0x33, 0x6d, + 0x78, 0x24, 0xee, 0xe3, 0xa2, 0x62, 0x24, 0x1a, 0x62, 0x73, 0x52, 0x3b, 0x09, 0xb8, + 0xd0, 0xce, 0x0d, 0x39, 0xe8, 0x60, 0xc9, 0x4d, 0x02, 0x53, 0x58, 0xdb, 0xdc, 0x25, + 0x92, 0xc7, 0xc6, 0x48, 0x0d, 0x39, 0xce, 0xbb, 0xa3 #endif }; @@ -209,7 +209,7 @@ void handleSetPlugin(uint8_t p1, switch (algorithmId) { case ECC_SECG_P256K1__ECDSA_SHA_256: curve = CX_CURVE_256K1; - verificationFn = (verificationAlgo*)cx_ecdsa_verify; + verificationFn = (verificationAlgo *) cx_ecdsa_verify; hashId = CX_SHA256; break; default: diff --git a/src_plugins/erc1155/erc1155_plugin.c b/src_plugins/erc1155/erc1155_plugin.c index 7eebd8c..dbc1b8e 100644 --- a/src_plugins/erc1155/erc1155_plugin.c +++ b/src_plugins/erc1155/erc1155_plugin.c @@ -139,4 +139,4 @@ void erc1155_plugin_call(int message, void *parameters) { } } -#endif // HAVE_NFT_SUPPORT +#endif // HAVE_NFT_SUPPORT diff --git a/src_plugins/erc1155/erc1155_plugin.h b/src_plugins/erc1155/erc1155_plugin.h index 22462a8..0702fdf 100644 --- a/src_plugins/erc1155/erc1155_plugin.h +++ b/src_plugins/erc1155/erc1155_plugin.h @@ -34,9 +34,9 @@ typedef enum { } erc1155_selector_field; typedef struct erc1155_context_t { - uint8_t address[ADDRESS_LENGTH]; - uint8_t tokenId[INT256_LENGTH]; - uint256_t value; + uint8_t address[ADDRESS_LENGTH]; + uint8_t tokenId[INT256_LENGTH]; + uint256_t value; uint16_t ids_array_len; uint32_t ids_offset; @@ -52,4 +52,4 @@ typedef struct erc1155_context_t { void handle_provide_parameter_1155(void *parameters); void handle_query_contract_ui_1155(void *parameters); -#endif // HAVE_NFT_SUPPORT +#endif // HAVE_NFT_SUPPORT diff --git a/src_plugins/erc1155/erc1155_provide_parameters.c b/src_plugins/erc1155/erc1155_provide_parameters.c index 59da428..800aafb 100644 --- a/src_plugins/erc1155/erc1155_provide_parameters.c +++ b/src_plugins/erc1155/erc1155_provide_parameters.c @@ -30,7 +30,7 @@ static void handle_safe_transfer(ethPluginProvideParameter_t *msg, erc1155_conte } static void handle_batch_transfer(ethPluginProvideParameter_t *msg, erc1155_context_t *context) { - uint256_t new_value; + uint256_t new_value; switch (context->next_param) { case FROM: @@ -41,23 +41,19 @@ static void handle_batch_transfer(ethPluginProvideParameter_t *msg, erc1155_cont context->next_param = TOKEN_IDS_OFFSET; break; case TOKEN_IDS_OFFSET: - context->ids_offset = \ - U4BE(msg->parameter, - PARAMETER_LENGTH - sizeof(context->ids_offset)) + 4; + context->ids_offset = + U4BE(msg->parameter, PARAMETER_LENGTH - sizeof(context->ids_offset)) + 4; context->next_param = VALUE_OFFSET; break; case VALUE_OFFSET: - context->values_offset = \ - U4BE(msg->parameter, - PARAMETER_LENGTH - sizeof(context->values_offset)) + 4; + context->values_offset = + U4BE(msg->parameter, PARAMETER_LENGTH - sizeof(context->values_offset)) + 4; context->next_param = TOKEN_IDS_LENGTH; break; case TOKEN_IDS_LENGTH: - if ((msg->parameterOffset + PARAMETER_LENGTH) > context->ids_offset) - { - context->ids_array_len = \ - U2BE(msg->parameter, - PARAMETER_LENGTH - sizeof(context->ids_array_len)); + if ((msg->parameterOffset + PARAMETER_LENGTH) > context->ids_offset) { + context->ids_array_len = + U2BE(msg->parameter, PARAMETER_LENGTH - sizeof(context->ids_array_len)); context->next_param = TOKEN_ID; // set to zero for next step context->array_index = 0; @@ -65,20 +61,16 @@ static void handle_batch_transfer(ethPluginProvideParameter_t *msg, erc1155_cont break; case TOKEN_ID: // don't copy anything since we won't display it - if (--context->ids_array_len == 0) - { + if (--context->ids_array_len == 0) { context->next_param = VALUE_LENGTH; } context->array_index++; break; case VALUE_LENGTH: - if ((msg->parameterOffset + PARAMETER_LENGTH) > context->values_offset) - { - context->values_array_len = \ - U2BE(msg->parameter, - PARAMETER_LENGTH - sizeof(context->values_array_len)); - if (context->values_array_len != context->array_index) - { + if ((msg->parameterOffset + PARAMETER_LENGTH) > context->values_offset) { + context->values_array_len = + U2BE(msg->parameter, PARAMETER_LENGTH - sizeof(context->values_array_len)); + if (context->values_array_len != context->array_index) { PRINTF("Token ids and values array sizes mismatch!"); } context->next_param = VALUE; @@ -92,8 +84,7 @@ static void handle_batch_transfer(ethPluginProvideParameter_t *msg, erc1155_cont copy_parameter(context->tokenId, msg->parameter, sizeof(context->value)); convertUint256BE(context->tokenId, sizeof(context->tokenId), &new_value); add256(&context->value, &new_value, &context->value); - if (--context->values_array_len == 0) - { + if (--context->values_array_len == 0) { context->next_param = NONE; } context->array_index++; @@ -132,10 +123,10 @@ void handle_provide_parameter_1155(void *parameters) { msg->result = ETH_PLUGIN_RESULT_SUCCESSFUL; - //if (context->targetOffset > SELECTOR_SIZE && - // context->targetOffset != msg->parameterOffset - SELECTOR_SIZE) { - // return; - //} + // if (context->targetOffset > SELECTOR_SIZE && + // context->targetOffset != msg->parameterOffset - SELECTOR_SIZE) { + // return; + // } switch (context->selectorIndex) { case SAFE_TRANSFER: handle_safe_transfer(msg, context); @@ -153,4 +144,4 @@ void handle_provide_parameter_1155(void *parameters) { } } -#endif // HAVE_NFT_SUPPORT +#endif // HAVE_NFT_SUPPORT diff --git a/src_plugins/erc1155/erc1155_ui.c b/src_plugins/erc1155/erc1155_ui.c index 5237a16..5b4e941 100644 --- a/src_plugins/erc1155/erc1155_ui.c +++ b/src_plugins/erc1155/erc1155_ui.c @@ -59,7 +59,7 @@ static void set_transfer_ui(ethQueryContractUI_t *msg, erc1155_context_t *contex break; case 2: strlcpy(msg->title, "NFT Address", msg->titleLength); - getEthDisplayableAddress((uint8_t *)msg->item1->nft.contractAddress, + getEthDisplayableAddress((uint8_t *) msg->item1->nft.contractAddress, msg->msg, msg->msgLength, &global_sha3, @@ -84,7 +84,7 @@ static void set_transfer_ui(ethQueryContractUI_t *msg, erc1155_context_t *contex } static void set_batch_transfer_ui(ethQueryContractUI_t *msg, erc1155_context_t *context) { - char quantity_str[48]; + char quantity_str[48]; switch (msg->screenIndex) { case 0: @@ -105,7 +105,7 @@ static void set_batch_transfer_ui(ethQueryContractUI_t *msg, erc1155_context_t * break; case 2: strlcpy(msg->title, "NFT Address", msg->titleLength); - getEthDisplayableAddress((uint8_t *)msg->item1->nft.contractAddress, + getEthDisplayableAddress((uint8_t *) msg->item1->nft.contractAddress, msg->msg, msg->msgLength, &global_sha3, @@ -149,4 +149,4 @@ void handle_query_contract_ui_1155(void *parameters) { } } -#endif // HAVE_NFT_SUPPORT +#endif // HAVE_NFT_SUPPORT diff --git a/src_plugins/erc721/erc721_plugin.c b/src_plugins/erc721/erc721_plugin.c index 4bdd232..badddb3 100644 --- a/src_plugins/erc721/erc721_plugin.c +++ b/src_plugins/erc721/erc721_plugin.c @@ -147,4 +147,4 @@ void erc721_plugin_call(int message, void *parameters) { } } -#endif // HAVE_NFT_SUPPORT +#endif // HAVE_NFT_SUPPORT diff --git a/src_plugins/erc721/erc721_plugin.h b/src_plugins/erc721/erc721_plugin.h index 2ab80df..1846609 100644 --- a/src_plugins/erc721/erc721_plugin.h +++ b/src_plugins/erc721/erc721_plugin.h @@ -43,4 +43,4 @@ typedef struct erc721_context_t { void handle_provide_parameter_721(void *parameters); void handle_query_contract_ui_721(void *parameters); -#endif // HAVE_NFT_SUPPORT +#endif // HAVE_NFT_SUPPORT diff --git a/src_plugins/erc721/erc721_provide_parameters.c b/src_plugins/erc721/erc721_provide_parameters.c index a315af0..ac5b870 100644 --- a/src_plugins/erc721/erc721_provide_parameters.c +++ b/src_plugins/erc721/erc721_provide_parameters.c @@ -94,4 +94,4 @@ void handle_provide_parameter_721(void *parameters) { } } -#endif // HAVE_NFT_SUPPORT +#endif // HAVE_NFT_SUPPORT diff --git a/src_plugins/erc721/erc721_ui.c b/src_plugins/erc721/erc721_ui.c index bb8264c..e312932 100644 --- a/src_plugins/erc721/erc721_ui.c +++ b/src_plugins/erc721/erc721_ui.c @@ -115,7 +115,7 @@ static void set_transfer_ui(ethQueryContractUI_t *msg, erc721_context_t *context break; case 2: strlcpy(msg->title, "NFT Address", msg->titleLength); - getEthDisplayableAddress((uint8_t *)msg->item1->nft.contractAddress, + getEthDisplayableAddress((uint8_t *) msg->item1->nft.contractAddress, msg->msg, msg->msgLength, &global_sha3, @@ -167,4 +167,4 @@ void handle_query_contract_ui_721(void *parameters) { } } -#endif // HAVE_NFT_SUPPORT +#endif // HAVE_NFT_SUPPORT From b124cd26b8a749651629adf5198bf7ca97662aa1 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 14 Jan 2022 15:34:23 +0100 Subject: [PATCH 8/9] Version bump to 1.9.17 --- CHANGELOG.md | 10 ++++++++-- Makefile | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c8def5..9b72ae0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [1.9.15](https://github.com/ledgerhq/app-ethereum/compare/1.9.14...1.9.15) - 2021-12-15 +## [1.9.17](https://github.com/ledgerhq/app-ethereum/compare/1.9.16...1.9.17) - 2022-01-14 ### Added -- Support for Non-Fungible Token (ERC-721 & ERC-1155) +- Support for Non-Fungible Tokens (ERC-721 & ERC-1155) + +## [1.9.16](https://github.com/ledgerhq/app-ethereum/compare/1.9.14...1.9.16) - 2022-01-13 + +### Added + +- Shyft variant ## [1.9.14](https://github.com/ledgerhq/app-ethereum/compare/1.9.13...1.9.14) - 2021-11-30 diff --git a/Makefile b/Makefile index fec5006..4607051 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ APP_LOAD_PARAMS += --path "1517992542'/1101353413'" APPVERSION_M=1 APPVERSION_N=9 -APPVERSION_P=16 +APPVERSION_P=17 APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) APP_LOAD_FLAGS= --appFlags 0x240 --dep Ethereum:$(APPVERSION) From 7d9d0fbbc645fc7820cf15eb2b946b1f83a49da8 Mon Sep 17 00:00:00 2001 From: Edouard Merle Date: Tue, 1 Feb 2022 16:48:36 +0100 Subject: [PATCH 9/9] fix: build for lns+ --- Makefile | 24 +++++++++++++----------- src/ui_flow.c | 2 +- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 4607051..0a1a209 100644 --- a/Makefile +++ b/Makefile @@ -248,10 +248,10 @@ APP_LOAD_PARAMS += $(APP_LOAD_FLAGS) --path "44'/1'" DEFINES += $(DEFINES_LIB) #prepare hsm generation -ifeq ($(TARGET_NAME), TARGET_NANOX) -ICONNAME=icons/nanox_app_$(CHAIN).gif -else +ifeq ($(TARGET_NAME),TARGET_NANOS) ICONNAME=icons/nanos_app_$(CHAIN).gif +else +ICONNAME=icons/nanox_app_$(CHAIN).gif endif ################ @@ -283,19 +283,21 @@ DEFINES += HAVE_UX_FLOW DEFINES += HAVE_WEBUSB WEBUSB_URL_SIZE_B=0 WEBUSB_URL="" ifeq ($(TARGET_NAME),TARGET_NANOX) -DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=300 DEFINES += HAVE_BLE BLE_COMMAND_TIMEOUT_MS=2000 DEFINES += HAVE_BLE_APDU # basic ledger apdu transport over BLE +endif +ifeq ($(TARGET_NAME),TARGET_NANOS) +DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=72 +DEFINES += HAVE_WALLET_ID_SDK +else +DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=300 DEFINES += HAVE_GLO096 DEFINES += HAVE_BAGL BAGL_WIDTH=128 BAGL_HEIGHT=64 DEFINES += HAVE_BAGL_ELLIPSIS # long label truncation feature DEFINES += HAVE_BAGL_FONT_OPEN_SANS_REGULAR_11PX DEFINES += HAVE_BAGL_FONT_OPEN_SANS_EXTRABOLD_11PX DEFINES += HAVE_BAGL_FONT_OPEN_SANS_LIGHT_16PX -else -DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=72 -DEFINES += HAVE_WALLET_ID_SDK endif # Enables direct data signing without having to specify it in the settings. Useful when testing with speculos. @@ -311,7 +313,7 @@ DEFINES += HAVE_BYPASS_SIGNATURES endif # NFTs -ifeq ($(TARGET_NAME), TARGET_NANOX) +ifneq ($(TARGET_NAME),TARGET_NANOS) DEFINES += HAVE_NFT_SUPPORT # Enable the NFT testing key NFT_TESTING_KEY:=0 @@ -325,10 +327,10 @@ endif DEBUG:=0 ifneq ($(DEBUG),0) DEFINES += HAVE_STACK_OVERFLOW_CHECK -ifeq ($(TARGET_NAME),TARGET_NANOX) -DEFINES += HAVE_PRINTF PRINTF=mcu_usb_printf -else +ifeq ($(TARGET_NAME),TARGET_NANOS) DEFINES += HAVE_PRINTF PRINTF=screen_printf +else +DEFINES += HAVE_PRINTF PRINTF=mcu_usb_printf endif else DEFINES += PRINTF\(...\)= diff --git a/src/ui_flow.c b/src/ui_flow.c index b6b2fa3..d6c0a16 100644 --- a/src/ui_flow.c +++ b/src/ui_flow.c @@ -171,7 +171,7 @@ UX_STEP_CB( "Error", "Blind signing must be enabled in Settings", }); -#elif defined(TARGET_NANOX) +#elif defined(TARGET_NANOX) || defined(TARGET_NANOS2) UX_STEP_CB( ux_warning_contract_data_step, pnn,