From afc52f20b40f5eb866ca6cea11767fea4d3eab3b Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 6 Dec 2024 18:34:48 +0100 Subject: [PATCH 01/45] Added missing mem_init at app start --- src/main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main.c b/src/main.c index c0ebeae74..8706f6bbc 100644 --- a/src/main.c +++ b/src/main.c @@ -36,6 +36,9 @@ #include "crypto_helpers.h" #include "manage_asset_info.h" #include "network_dynamic.h" +#ifdef HAVE_DYN_MEM_ALLOC +#include "mem.h" +#endif tmpCtx_t tmpCtx; txContext_t txContext; @@ -366,6 +369,9 @@ void coin_main(eth_libargs_t *args) { io_init(); ui_idle(); +#ifdef HAVE_DYN_MEM_ALLOC + mem_init(); +#endif #ifdef HAVE_TRUSTED_NAME // to prevent it from having a fixed value at boot From ae626b86ff5d79c2e16d2abd37d9eadee8051af0 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 9 Dec 2024 15:34:57 +0100 Subject: [PATCH 02/45] mem_dealloc can now print a warning --- src/mem.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/mem.c b/src/mem.c index 8de2e52ea..cefdb60be 100644 --- a/src/mem.c +++ b/src/mem.c @@ -41,8 +41,8 @@ void mem_reset(void) { * @return Allocated memory pointer; \ref NULL if not enough space left. */ void *mem_alloc(size_t size) { - if ((mem_idx + size) > SIZE_MEM_BUFFER) // Buffer exceeded - { + // Buffer exceeded + if ((mem_idx + size) > sizeof(mem_buffer)) { PRINTF("Error: mem_alloc(%u) failed!\n", size); return NULL; } @@ -56,8 +56,9 @@ void *mem_alloc(size_t size) { * @param[in] size Requested deallocation size in bytes */ void mem_dealloc(size_t size) { - if (size > mem_idx) // More than is already allocated - { + // More than is already allocated + if (size > mem_idx) { + PRINTF("Warning: mem_dealloc(%u) with a value larger than allocated!\n", size); mem_idx = 0; } else { mem_idx -= size; From 084eac505605afe4a00dfe21def9cbea8d6a73d4 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 16 Oct 2024 18:51:43 +0200 Subject: [PATCH 03/45] Small refactoring of the handler of the sign transaction APDU --- src_features/signTx/cmd_signTx.c | 130 ++++++++++++++++++------------- 1 file changed, 76 insertions(+), 54 deletions(-) diff --git a/src_features/signTx/cmd_signTx.c b/src_features/signTx/cmd_signTx.c index e74f5971f..9dedf7f4d 100644 --- a/src_features/signTx/cmd_signTx.c +++ b/src_features/signTx/cmd_signTx.c @@ -3,74 +3,96 @@ #include "feature_signTx.h" #include "eth_plugin_interface.h" -uint16_t handleSign(uint8_t p1, - uint8_t p2, - const uint8_t *workBuffer, - uint8_t dataLength, - unsigned int *flags) { - parserStatus_e txResult; - uint16_t sw = APDU_NO_RESPONSE; +typedef enum { + SIGN_MODE_BASIC = 0, +} e_sign_mode; + +static uint16_t handle_first_sign_chunk(const uint8_t *payload, uint8_t length, uint8_t *offset) { + uint8_t length_tmp = length; + uint8_t tx_type; cx_err_t error = CX_INTERNAL_ERROR; - if (p1 == P1_FIRST) { - if (appState != APP_STATE_IDLE) { - reset_app_context(); - } - appState = APP_STATE_SIGNING_TX; + if (appState != APP_STATE_IDLE) { + reset_app_context(); + } + appState = APP_STATE_SIGNING_TX; - workBuffer = parseBip32(workBuffer, &dataLength, &tmpCtx.transactionContext.bip32); - if (workBuffer == NULL) { - return APDU_RESPONSE_INVALID_DATA; - } + if (parseBip32(&payload[*offset], &length_tmp, &tmpCtx.transactionContext.bip32) == NULL) { + return APDU_RESPONSE_INVALID_DATA; + } + *offset += (length - length_tmp); - tmpContent.txContent.dataPresent = false; - dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_UNAVAILABLE; + tmpContent.txContent.dataPresent = false; + dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_UNAVAILABLE; - if (initTx(&txContext, &global_sha3, &tmpContent.txContent, customProcessor, NULL) == - false) { - return APDU_RESPONSE_INVALID_DATA; - } - if (dataLength < 1) { - PRINTF("Invalid data\n"); - return APDU_RESPONSE_INVALID_DATA; - } + if (initTx(&txContext, &global_sha3, &tmpContent.txContent, customProcessor, NULL) == false) { + return APDU_RESPONSE_INVALID_DATA; + } + if (*offset >= length) { + PRINTF("Invalid data\n"); + return APDU_RESPONSE_INVALID_DATA; + } - // EIP 2718: TransactionType might be present before the TransactionPayload. - uint8_t txType = *workBuffer; - if (txType >= MIN_TX_TYPE && txType <= MAX_TX_TYPE) { - // Enumerate through all supported txTypes here... - if (txType == EIP2930 || txType == EIP1559) { - error = cx_hash_no_throw((cx_hash_t *) &global_sha3, 0, workBuffer, 1, NULL, 0); - if (error != CX_OK) { - return error; - } - txContext.txType = txType; - workBuffer++; - dataLength--; - } else { - PRINTF("Transaction type %d not supported\n", txType); + // EIP 2718: TransactionType might be present before the TransactionPayload. + tx_type = payload[*offset]; + if (tx_type <= MAX_TX_TYPE) { + switch (tx_type) { + case EIP1559: + case EIP2930: + break; + default: + PRINTF("Transaction type %d not supported\n", tx_type); return APDU_RESPONSE_TX_TYPE_NOT_SUPPORTED; - } - } else { - txContext.txType = LEGACY; } - PRINTF("TxType: %x\n", txContext.txType); - } else if (p1 != P1_MORE) { - return APDU_RESPONSE_INVALID_P1_P2; - } - if (p2 != 0) { - return APDU_RESPONSE_INVALID_P1_P2; + error = cx_hash_no_throw((cx_hash_t *) &global_sha3, 0, &tx_type, sizeof(tx_type), NULL, 0); + if (error != CX_OK) { + return error; + } + txContext.txType = tx_type; + *offset += sizeof(tx_type); + } else { + txContext.txType = LEGACY; } - if ((p1 == P1_MORE) && (appState != APP_STATE_SIGNING_TX)) { - PRINTF("Signature not initialized\n"); - return APDU_RESPONSE_CONDITION_NOT_SATISFIED; + PRINTF("TxType: %x\n", txContext.txType); + return APDU_NO_RESPONSE; +} + +uint16_t handleSign(uint8_t p1, + uint8_t p2, + const uint8_t *workBuffer, + uint8_t dataLength, + unsigned int *flags) { + uint16_t sw = APDU_NO_RESPONSE; + uint8_t offset = 0; + + switch (p2) { + case SIGN_MODE_BASIC: + switch (p1) { + case P1_FIRST: + if ((sw = handle_first_sign_chunk(workBuffer, dataLength, &offset)) != + APDU_NO_RESPONSE) { + return sw; + } + break; + case P1_MORE: + if (appState != APP_STATE_SIGNING_TX) { + PRINTF("Signature not initialized\n"); + return APDU_RESPONSE_CONDITION_NOT_SATISFIED; + } + break; + default: + return APDU_RESPONSE_INVALID_P1_P2; + } + break; + default: + return APDU_RESPONSE_INVALID_P1_P2; } + if (txContext.currentField == RLP_NONE) { PRINTF("Parser not initialized\n"); return APDU_RESPONSE_CONDITION_NOT_SATISFIED; } - txResult = processTx(&txContext, workBuffer, dataLength); - switch (txResult) { + switch (processTx(&txContext, &workBuffer[offset], dataLength - offset)) { case USTREAM_SUSPENDED: break; case USTREAM_FINISHED: From 8536e21028291a47ceb3f83ffcfefb15706d549a Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 17 Oct 2024 14:00:15 +0200 Subject: [PATCH 04/45] Now returns an error RAPDU if the hashing of the TxType failed --- src_features/signTx/cmd_signTx.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src_features/signTx/cmd_signTx.c b/src_features/signTx/cmd_signTx.c index 9dedf7f4d..be917debd 100644 --- a/src_features/signTx/cmd_signTx.c +++ b/src_features/signTx/cmd_signTx.c @@ -10,7 +10,6 @@ typedef enum { static uint16_t handle_first_sign_chunk(const uint8_t *payload, uint8_t length, uint8_t *offset) { uint8_t length_tmp = length; uint8_t tx_type; - cx_err_t error = CX_INTERNAL_ERROR; if (appState != APP_STATE_IDLE) { reset_app_context(); @@ -44,9 +43,9 @@ static uint16_t handle_first_sign_chunk(const uint8_t *payload, uint8_t length, PRINTF("Transaction type %d not supported\n", tx_type); return APDU_RESPONSE_TX_TYPE_NOT_SUPPORTED; } - error = cx_hash_no_throw((cx_hash_t *) &global_sha3, 0, &tx_type, sizeof(tx_type), NULL, 0); - if (error != CX_OK) { - return error; + if (cx_hash_no_throw((cx_hash_t *) &global_sha3, 0, &tx_type, sizeof(tx_type), NULL, 0) != + CX_OK) { + return APDU_RESPONSE_INVALID_DATA; } txContext.txType = tx_type; *offset += sizeof(tx_type); From 33bded6a262851572cffc5a05d22c4a6cdac7933 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 16 Dec 2024 09:32:46 +0100 Subject: [PATCH 05/45] Small change in variable naming --- src_features/signTx/cmd_signTx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src_features/signTx/cmd_signTx.c b/src_features/signTx/cmd_signTx.c index be917debd..7cc70e638 100644 --- a/src_features/signTx/cmd_signTx.c +++ b/src_features/signTx/cmd_signTx.c @@ -58,8 +58,8 @@ static uint16_t handle_first_sign_chunk(const uint8_t *payload, uint8_t length, uint16_t handleSign(uint8_t p1, uint8_t p2, - const uint8_t *workBuffer, - uint8_t dataLength, + const uint8_t *payload, + uint8_t length, unsigned int *flags) { uint16_t sw = APDU_NO_RESPONSE; uint8_t offset = 0; @@ -68,7 +68,7 @@ uint16_t handleSign(uint8_t p1, case SIGN_MODE_BASIC: switch (p1) { case P1_FIRST: - if ((sw = handle_first_sign_chunk(workBuffer, dataLength, &offset)) != + if ((sw = handle_first_sign_chunk(payload, length, &offset)) != APDU_NO_RESPONSE) { return sw; } @@ -91,7 +91,7 @@ uint16_t handleSign(uint8_t p1, PRINTF("Parser not initialized\n"); return APDU_RESPONSE_CONDITION_NOT_SATISFIED; } - switch (processTx(&txContext, &workBuffer[offset], dataLength - offset)) { + switch (processTx(&txContext, &payload[offset], length - offset)) { case USTREAM_SUSPENDED: break; case USTREAM_FINISHED: From fd3d72b7f371e23fcfafcdbe208e21a6c5acf82b Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 16 Dec 2024 09:34:01 +0100 Subject: [PATCH 06/45] Renamed some functions, removed useless fields from the txContent struct --- src/ethUstream.c | 60 +++++++++++--------------- src/ethUstream.h | 14 +----- src_features/signTx/cmd_signTx.c | 6 +-- src_features/signTx/feature_signTx.h | 5 ++- src_features/signTx/logic_signTx.c | 2 +- src_features/signTx/ui_common_signTx.c | 2 +- 6 files changed, 35 insertions(+), 54 deletions(-) diff --git a/src/ethUstream.c b/src/ethUstream.c index eebf8e993..fb10ee455 100644 --- a/src/ethUstream.c +++ b/src/ethUstream.c @@ -21,9 +21,7 @@ #include "ethUstream.h" #include "rlp_utils.h" #include "common_utils.h" - -#define MAX_INT256 32 -#define MAX_ADDRESS 20 +#include "feature_signTx.h" static bool check_fields(txContext_t *context, const char *name, uint32_t length) { UNUSED(name); // Just for the case where DEBUG is not enabled @@ -56,16 +54,10 @@ static bool check_cmd_length(txContext_t *context, const char *name, uint32_t le return true; } -bool initTx(txContext_t *context, - cx_sha3_t *sha3, - txContent_t *content, - ustreamProcess_t customProcessor, - void *extra) { - memset(context, 0, sizeof(txContext_t)); +bool init_tx(txContext_t *context, cx_sha3_t *sha3, txContent_t *content) { + explicit_bzero(context, sizeof(*context)); context->sha3 = sha3; context->content = content; - context->customProcessor = customProcessor; - context->extra = extra; context->currentField = RLP_NONE + 1; if (cx_keccak_init_no_throw(context->sha3, 256) != CX_OK) { return false; @@ -150,7 +142,7 @@ static bool processAccessList(txContext_t *context) { } static bool processChainID(txContext_t *context) { - if (check_fields(context, "RLP_CHAINID", MAX_INT256) == false) { + if (check_fields(context, "RLP_CHAINID", INT256_LENGTH) == false) { return false; } @@ -170,7 +162,7 @@ static bool processChainID(txContext_t *context) { } static bool processNonce(txContext_t *context) { - if (check_fields(context, "RLP_NONCE", MAX_INT256) == false) { + if (check_fields(context, "RLP_NONCE", INT256_LENGTH) == false) { return false; } @@ -190,7 +182,7 @@ static bool processNonce(txContext_t *context) { } static bool processStartGas(txContext_t *context) { - if (check_fields(context, "RLP_STARTGAS", MAX_INT256) == false) { + if (check_fields(context, "RLP_STARTGAS", INT256_LENGTH) == false) { return false; } @@ -217,7 +209,7 @@ static bool processGasLimit(txContext_t *context) { } static bool processGasprice(txContext_t *context) { - if (check_fields(context, "RLP_GASPRICE", MAX_INT256) == false) { + if (check_fields(context, "RLP_GASPRICE", INT256_LENGTH) == false) { return false; } @@ -239,7 +231,7 @@ static bool processGasprice(txContext_t *context) { } static bool processValue(txContext_t *context) { - if (check_fields(context, "RLP_VALUE", MAX_INT256) == false) { + if (check_fields(context, "RLP_VALUE", INT256_LENGTH) == false) { return false; } @@ -261,7 +253,7 @@ static bool processValue(txContext_t *context) { } static bool processTo(txContext_t *context) { - if (check_fields(context, "RLP_TO", MAX_ADDRESS) == false) { + if (check_fields(context, "RLP_TO", ADDRESS_LENGTH) == false) { return false; } @@ -540,22 +532,20 @@ static parserStatus_e processTxInternal(txContext_t *context) { return status; } } - if (context->customProcessor != NULL) { - customStatus = context->customProcessor(context); - PRINTF("After customprocessor\n"); - switch (customStatus) { - case CUSTOM_NOT_HANDLED: - case CUSTOM_HANDLED: - break; - case CUSTOM_SUSPENDED: - return USTREAM_SUSPENDED; - case CUSTOM_FAULT: - PRINTF("Custom processor aborted\n"); - return USTREAM_FAULT; - default: - PRINTF("Unhandled custom processor status\n"); - return USTREAM_FAULT; - } + customStatus = customProcessor(context); + PRINTF("After customprocessor\n"); + switch (customStatus) { + case CUSTOM_NOT_HANDLED: + case CUSTOM_HANDLED: + break; + case CUSTOM_SUSPENDED: + return USTREAM_SUSPENDED; + case CUSTOM_FAULT: + PRINTF("Custom processor aborted\n"); + return USTREAM_FAULT; + default: + PRINTF("Unhandled custom processor status\n"); + return USTREAM_FAULT; } if (customStatus == CUSTOM_NOT_HANDLED) { PRINTF("Current field: %d\n", context->currentField); @@ -584,8 +574,8 @@ static parserStatus_e processTxInternal(txContext_t *context) { PRINTF("end of here\n"); } -parserStatus_e processTx(txContext_t *context, const uint8_t *buffer, uint32_t length) { - context->workBuffer = buffer; +parserStatus_e process_tx(txContext_t *context, const uint8_t *payload, size_t length) { + context->workBuffer = payload; context->commandLength = length; return processTxInternal(context); } diff --git a/src/ethUstream.h b/src/ethUstream.h index 3548af985..7f491a4d8 100644 --- a/src/ethUstream.h +++ b/src/ethUstream.h @@ -25,8 +25,6 @@ #include "common_utils.h" #include "tx_content.h" -struct txContext_t; - typedef enum customStatus_e { CUSTOM_NOT_HANDLED, CUSTOM_HANDLED, @@ -34,8 +32,6 @@ typedef enum customStatus_e { CUSTOM_FAULT } customStatus_e; -typedef customStatus_e (*ustreamProcess_t)(struct txContext_t *context); - // First variant of every Tx enum. #define RLP_NONE 0 @@ -120,17 +116,11 @@ typedef struct txContext_t { uint32_t rlpBufferPos; const uint8_t *workBuffer; uint32_t commandLength; - ustreamProcess_t customProcessor; txContent_t *content; - void *extra; uint8_t txType; } txContext_t; -bool initTx(txContext_t *context, - cx_sha3_t *sha3, - txContent_t *content, - ustreamProcess_t customProcessor, - void *extra); -parserStatus_e processTx(txContext_t *context, const uint8_t *buffer, uint32_t length); +bool init_tx(txContext_t *context, cx_sha3_t *sha3, txContent_t *content); +parserStatus_e process_tx(txContext_t *context, const uint8_t *buffer, size_t length); parserStatus_e continueTx(txContext_t *context); bool copyTxData(txContext_t *context, uint8_t *out, uint32_t length); diff --git a/src_features/signTx/cmd_signTx.c b/src_features/signTx/cmd_signTx.c index 7cc70e638..4eedc40fe 100644 --- a/src_features/signTx/cmd_signTx.c +++ b/src_features/signTx/cmd_signTx.c @@ -24,7 +24,7 @@ static uint16_t handle_first_sign_chunk(const uint8_t *payload, uint8_t length, tmpContent.txContent.dataPresent = false; dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_UNAVAILABLE; - if (initTx(&txContext, &global_sha3, &tmpContent.txContent, customProcessor, NULL) == false) { + if (init_tx(&txContext, &global_sha3, &tmpContent.txContent) == false) { return APDU_RESPONSE_INVALID_DATA; } if (*offset >= length) { @@ -91,11 +91,11 @@ uint16_t handleSign(uint8_t p1, PRINTF("Parser not initialized\n"); return APDU_RESPONSE_CONDITION_NOT_SATISFIED; } - switch (processTx(&txContext, &payload[offset], length - offset)) { + switch (process_tx(&txContext, &payload[offset], length - offset)) { case USTREAM_SUSPENDED: break; case USTREAM_FINISHED: - sw = finalizeParsing(); + sw = finalize_parsing(); break; case USTREAM_PROCESSING: return APDU_RESPONSE_OK; diff --git a/src_features/signTx/feature_signTx.h b/src_features/signTx/feature_signTx.h index f1c83df42..9deb4173b 100644 --- a/src_features/signTx/feature_signTx.h +++ b/src_features/signTx/feature_signTx.h @@ -1,7 +1,8 @@ #ifndef _SIGN_TX_H_ #define _SIGN_TX_H_ -#include "shared_context.h" +#include +#include "ethUstream.h" // Error codes for swap, to be moved in SDK? #define ERROR_WRONG_AMOUNT 0x01 @@ -27,7 +28,7 @@ typedef enum { } plugin_ui_state_t; customStatus_e customProcessor(txContext_t *context); -uint16_t finalizeParsing(); +uint16_t finalize_parsing(void); void ux_approve_tx(bool fromPlugin); void start_signature_flow(void); diff --git a/src_features/signTx/logic_signTx.c b/src_features/signTx/logic_signTx.c index b50642ebd..ad07bc901 100644 --- a/src_features/signTx/logic_signTx.c +++ b/src_features/signTx/logic_signTx.c @@ -633,7 +633,7 @@ void start_signature_flow(void) { } } -uint16_t finalizeParsing(void) { +uint16_t finalize_parsing(void) { uint16_t sw = APDU_RESPONSE_UNKNOWN; g_use_standard_ui = true; diff --git a/src_features/signTx/ui_common_signTx.c b/src_features/signTx/ui_common_signTx.c index c29f3672e..9e8b4713f 100644 --- a/src_features/signTx/ui_common_signTx.c +++ b/src_features/signTx/ui_common_signTx.c @@ -80,7 +80,7 @@ unsigned int io_seproxyhal_touch_data_ok(void) { case USTREAM_SUSPENDED: break; case USTREAM_FINISHED: - err = finalizeParsing(); + err = finalize_parsing(); break; case USTREAM_PROCESSING: err = io_seproxyhal_send_status(APDU_RESPONSE_OK, 0, false, true); From 526e80341eaef6ca0b1b7b8cc05c983fbe3a8504 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 17 Oct 2024 18:41:18 +0200 Subject: [PATCH 07/45] Moved ethUstream and rlp_utils from the signTx feature folder --- {src => src_features/signTx}/ethUstream.c | 0 {src => src_features/signTx}/ethUstream.h | 0 {src => src_features/signTx}/rlp_utils.c | 0 {src => src_features/signTx}/rlp_utils.h | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename {src => src_features/signTx}/ethUstream.c (100%) rename {src => src_features/signTx}/ethUstream.h (100%) rename {src => src_features/signTx}/rlp_utils.c (100%) rename {src => src_features/signTx}/rlp_utils.h (100%) diff --git a/src/ethUstream.c b/src_features/signTx/ethUstream.c similarity index 100% rename from src/ethUstream.c rename to src_features/signTx/ethUstream.c diff --git a/src/ethUstream.h b/src_features/signTx/ethUstream.h similarity index 100% rename from src/ethUstream.h rename to src_features/signTx/ethUstream.h diff --git a/src/rlp_utils.c b/src_features/signTx/rlp_utils.c similarity index 100% rename from src/rlp_utils.c rename to src_features/signTx/rlp_utils.c diff --git a/src/rlp_utils.h b/src_features/signTx/rlp_utils.h similarity index 100% rename from src/rlp_utils.h rename to src_features/signTx/rlp_utils.h From 463a0250f635597977f0519235a128657d13d678 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 18 Oct 2024 11:36:06 +0200 Subject: [PATCH 08/45] Restrict include of signTx header files to where necessary --- src_bagl/ui_flow_signTx.c | 1 - src_features/signMessageEIP712/context_712.h | 2 +- src_features/signMessageEIP712/filtering.c | 1 - src_nbgl/ui_blind_signing.c | 1 - src_plugins/erc1155/erc1155_plugin.h | 1 - src_plugins/erc20/erc20_plugin.c | 1 - src_plugins/erc721/erc721_plugin.h | 1 - 7 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src_bagl/ui_flow_signTx.c b/src_bagl/ui_flow_signTx.c index d25e6c34e..0279ad748 100644 --- a/src_bagl/ui_flow_signTx.c +++ b/src_bagl/ui_flow_signTx.c @@ -2,7 +2,6 @@ #include "ui_callbacks.h" #include "chainConfig.h" #include "common_utils.h" -#include "feature_signTx.h" #include "network.h" #include "eth_plugin_handler.h" #include "ui_plugin.h" diff --git a/src_features/signMessageEIP712/context_712.h b/src_features/signMessageEIP712/context_712.h index 2b020973f..b01707484 100644 --- a/src_features/signMessageEIP712/context_712.h +++ b/src_features/signMessageEIP712/context_712.h @@ -4,7 +4,7 @@ #ifdef HAVE_EIP712_FULL_SUPPORT #include -#include "ethUstream.h" // ADDRESS_LENGTH +#include "common_utils.h" typedef struct { uint8_t contract_addr[ADDRESS_LENGTH]; diff --git a/src_features/signMessageEIP712/filtering.c b/src_features/signMessageEIP712/filtering.c index 5a03e8401..232043679 100644 --- a/src_features/signMessageEIP712/filtering.c +++ b/src_features/signMessageEIP712/filtering.c @@ -2,7 +2,6 @@ #include "filtering.h" #include "hash_bytes.h" -#include "ethUstream.h" // INT256_LENGTH #include "apdu_constants.h" // APDU return codes #include "public_keys.h" #include "manage_asset_info.h" diff --git a/src_nbgl/ui_blind_signing.c b/src_nbgl/ui_blind_signing.c index ba0a40fb8..d5ad753c8 100644 --- a/src_nbgl/ui_blind_signing.c +++ b/src_nbgl/ui_blind_signing.c @@ -1,7 +1,6 @@ #include #include "shared_context.h" #include "ui_callbacks.h" -#include "feature_signTx.h" #include "ui_nbgl.h" #include "apdu_constants.h" #include "context_712.h" diff --git a/src_plugins/erc1155/erc1155_plugin.h b/src_plugins/erc1155/erc1155_plugin.h index d9615dfeb..56908b2e9 100644 --- a/src_plugins/erc1155/erc1155_plugin.h +++ b/src_plugins/erc1155/erc1155_plugin.h @@ -5,7 +5,6 @@ #include #include -#include "ethUstream.h" #include "uint256.h" #include "asset_info.h" #include "eth_plugin_interface.h" diff --git a/src_plugins/erc20/erc20_plugin.c b/src_plugins/erc20/erc20_plugin.c index 13daf29f2..c7a5af884 100644 --- a/src_plugins/erc20/erc20_plugin.c +++ b/src_plugins/erc20/erc20_plugin.c @@ -3,7 +3,6 @@ #include "eth_plugin_handler.h" #include "shared_context.h" #include "plugin_utils.h" -#include "ethUstream.h" #include "common_utils.h" typedef enum { ERC20_TRANSFER = 0, ERC20_APPROVE } erc20Selector_t; diff --git a/src_plugins/erc721/erc721_plugin.h b/src_plugins/erc721/erc721_plugin.h index 42f1df68e..bf1d1edeb 100644 --- a/src_plugins/erc721/erc721_plugin.h +++ b/src_plugins/erc721/erc721_plugin.h @@ -5,7 +5,6 @@ #include #include -#include "ethUstream.h" #include "asset_info.h" #include "eth_plugin_interface.h" From 8b7d4960c86c0024ab944de8fab61493fe4bbc0c Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 20 Nov 2024 17:12:12 +0100 Subject: [PATCH 09/45] New time formatting functions --- src/time_format.c | 52 +++++++++++++++++++++++ src/time_format.h | 11 +++++ src_features/signMessageEIP712/ui_logic.c | 26 +----------- 3 files changed, 65 insertions(+), 24 deletions(-) create mode 100644 src/time_format.c create mode 100644 src/time_format.h diff --git a/src/time_format.c b/src/time_format.c new file mode 100644 index 000000000..ec5a53baa --- /dev/null +++ b/src/time_format.c @@ -0,0 +1,52 @@ +#include +#include "time_format.h" + +static bool get_time_struct(const time_t *timestamp, struct tm *tstruct) { + if (gmtime_r(timestamp, tstruct) == NULL) { + return false; + } + return true; +} + +bool time_format_to_yyyymmdd(const time_t *timestamp, char *out, size_t out_size) { + struct tm tstruct; + + if (!get_time_struct(timestamp, &tstruct)) { + return false; + } + snprintf(out, + out_size, + "%04d-%02d-%02d", + tstruct.tm_year + 1900, + tstruct.tm_mon + 1, + tstruct.tm_mday); + return true; +} + +bool time_format_to_utc(const time_t *timestamp, char *out, size_t out_size) { + struct tm tstruct; + int shown_hour; + + if (!get_time_struct(timestamp, &tstruct)) { + return false; + } + if (tstruct.tm_hour == 0) { + shown_hour = 12; + } else { + shown_hour = tstruct.tm_hour; + if (shown_hour > 12) { + shown_hour -= 12; + } + } + snprintf(out, + out_size, + "%04d-%02d-%02d\n%02d:%02d:%02d %s UTC", + tstruct.tm_year + 1900, + tstruct.tm_mon + 1, + tstruct.tm_mday, + shown_hour, + tstruct.tm_min, + tstruct.tm_sec, + (tstruct.tm_hour < 12) ? "AM" : "PM"); + return true; +} diff --git a/src/time_format.h b/src/time_format.h new file mode 100644 index 000000000..b939c179d --- /dev/null +++ b/src/time_format.h @@ -0,0 +1,11 @@ +#ifndef TIME_FORMAT_H_ +#define TIME_FORMAT_H_ + +#include +#include +#include + +bool time_format_to_yyyymmdd(const time_t *timestamp, char *out, size_t out_size); +bool time_format_to_utc(const time_t *timestamp, char *out, size_t out_size); + +#endif // !TIME_FORMAT_H_ diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 901e9c637..d022fcb13 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -21,6 +21,7 @@ #include "filtering.h" #include "trusted_name.h" #include "network.h" +#include "time_format.h" #define AMOUNT_JOIN_FLAG_TOKEN (1 << 0) #define AMOUNT_JOIN_FLAG_VALUE (1 << 1) @@ -556,32 +557,9 @@ static bool ui_712_format_trusted_name(const uint8_t *data, uint8_t length) { * @return whether it was successful or not */ static bool ui_712_format_datetime(const uint8_t *data, uint8_t length) { - struct tm tstruct; - int shown_hour; time_t timestamp = u64_from_BE(data, length); - if (gmtime_r(×tamp, &tstruct) == NULL) { - return false; - } - if (tstruct.tm_hour == 0) { - shown_hour = 12; - } else { - shown_hour = tstruct.tm_hour; - if (shown_hour > 12) { - shown_hour -= 12; - } - } - snprintf(strings.tmp.tmp, - sizeof(strings.tmp.tmp), - "%04d-%02d-%02d\n%02d:%02d:%02d %s UTC", - tstruct.tm_year + 1900, - tstruct.tm_mon + 1, - tstruct.tm_mday, - shown_hour, - tstruct.tm_min, - tstruct.tm_sec, - (tstruct.tm_hour < 12) ? "AM" : "PM"); - return true; + return time_format_to_utc(×tamp, strings.tmp.tmp, sizeof(strings.tmp.tmp)); } /** From b5c69ac05e44612154e9f9d97a23020358ca4fbb Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 16 Dec 2024 09:36:58 +0100 Subject: [PATCH 10/45] Support for new TX sign modes --- makefile_conf/features.mk | 4 ++++ src_features/signTx/cmd_signTx.c | 29 ++++++++++++++++++++++++++--- src_features/signTx/ethUstream.c | 7 ++++++- src_features/signTx/ethUstream.h | 7 ++++++- 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/makefile_conf/features.mk b/makefile_conf/features.mk index 8d1ab1ea3..ef5b86bdf 100644 --- a/makefile_conf/features.mk +++ b/makefile_conf/features.mk @@ -36,6 +36,10 @@ ifneq ($(TARGET_NAME),TARGET_NANOS) DEFINES += HAVE_EIP712_FULL_SUPPORT endif +ifneq ($(TARGET_NAME),TARGET_NANOS) + DEFINES += HAVE_GENERIC_TX_PARSER +endif + # CryptoAssetsList key CAL_TEST_KEY ?= 0 ifneq ($(CAL_TEST_KEY),0) diff --git a/src_features/signTx/cmd_signTx.c b/src_features/signTx/cmd_signTx.c index 4eedc40fe..69fa7b782 100644 --- a/src_features/signTx/cmd_signTx.c +++ b/src_features/signTx/cmd_signTx.c @@ -2,12 +2,18 @@ #include "apdu_constants.h" #include "feature_signTx.h" #include "eth_plugin_interface.h" +#include "apdu_constants.h" typedef enum { SIGN_MODE_BASIC = 0, + SIGN_MODE_STORE = 1, + SIGN_MODE_START_FLOW = 2, } e_sign_mode; -static uint16_t handle_first_sign_chunk(const uint8_t *payload, uint8_t length, uint8_t *offset) { +static uint16_t handle_first_sign_chunk(const uint8_t *payload, + uint8_t length, + uint8_t *offset, + e_sign_mode mode) { uint8_t length_tmp = length; uint8_t tx_type; @@ -24,7 +30,8 @@ static uint16_t handle_first_sign_chunk(const uint8_t *payload, uint8_t length, tmpContent.txContent.dataPresent = false; dataContext.tokenContext.pluginStatus = ETH_PLUGIN_RESULT_UNAVAILABLE; - if (init_tx(&txContext, &global_sha3, &tmpContent.txContent) == false) { + if (init_tx(&txContext, &global_sha3, &tmpContent.txContent, mode == SIGN_MODE_STORE) == + false) { return APDU_RESPONSE_INVALID_DATA; } if (*offset >= length) { @@ -66,9 +73,12 @@ uint16_t handleSign(uint8_t p1, switch (p2) { case SIGN_MODE_BASIC: +#ifdef HAVE_GENERIC_TX_PARSER + case SIGN_MODE_STORE: +#endif switch (p1) { case P1_FIRST: - if ((sw = handle_first_sign_chunk(payload, length, &offset)) != + if ((sw = handle_first_sign_chunk(payload, length, &offset, p2)) != APDU_NO_RESPONSE) { return sw; } @@ -83,6 +93,19 @@ uint16_t handleSign(uint8_t p1, return APDU_RESPONSE_INVALID_P1_P2; } break; +#ifdef HAVE_GENERIC_TX_PARSER + case SIGN_MODE_START_FLOW: + if (appState != APP_STATE_SIGNING_TX) { + PRINTF("Signature not initialized\n"); + return APDU_RESPONSE_CONDITION_NOT_SATISFIED; + } + if (length != 0) { + return APDU_RESPONSE_INVALID_DATA; + } + // TODO: check hash + *flags |= IO_ASYNCH_REPLY; + return APDU_NO_RESPONSE; +#endif default: return APDU_RESPONSE_INVALID_P1_P2; } diff --git a/src_features/signTx/ethUstream.c b/src_features/signTx/ethUstream.c index fb10ee455..a31dce62a 100644 --- a/src_features/signTx/ethUstream.c +++ b/src_features/signTx/ethUstream.c @@ -54,11 +54,16 @@ static bool check_cmd_length(txContext_t *context, const char *name, uint32_t le return true; } -bool init_tx(txContext_t *context, cx_sha3_t *sha3, txContent_t *content) { +bool init_tx(txContext_t *context, cx_sha3_t *sha3, txContent_t *content, bool store_calldata) { explicit_bzero(context, sizeof(*context)); context->sha3 = sha3; context->content = content; context->currentField = RLP_NONE + 1; +#ifdef HAVE_GENERIC_TX_PARSER + context->store_calldata = store_calldata; +#else + UNUSED(store_calldata); +#endif if (cx_keccak_init_no_throw(context->sha3, 256) != CX_OK) { return false; } diff --git a/src_features/signTx/ethUstream.h b/src_features/signTx/ethUstream.h index 7f491a4d8..b30a5940c 100644 --- a/src_features/signTx/ethUstream.h +++ b/src_features/signTx/ethUstream.h @@ -118,9 +118,14 @@ typedef struct txContext_t { uint32_t commandLength; txContent_t *content; uint8_t txType; + bool rlp_size_known; + uint32_t remaining_rlp_size; +#ifdef HAVE_GENERIC_TX_PARSER + bool store_calldata; +#endif } txContext_t; -bool init_tx(txContext_t *context, cx_sha3_t *sha3, txContent_t *content); +bool init_tx(txContext_t *context, cx_sha3_t *sha3, txContent_t *content, bool store_calldata); parserStatus_e process_tx(txContext_t *context, const uint8_t *buffer, size_t length); parserStatus_e continueTx(txContext_t *context); bool copyTxData(txContext_t *context, uint8_t *out, uint32_t length); From 2c96cf88303dab9a5295f10d4065353d38a6aa23 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 21 Oct 2024 18:03:49 +0200 Subject: [PATCH 11/45] Now stores calldata in RAM --- src_features/generic_tx_parser/calldata.c | 65 +++++++++++++++++++++++ src_features/generic_tx_parser/calldata.h | 17 ++++++ src_features/signTx/ethUstream.c | 13 +++++ 3 files changed, 95 insertions(+) create mode 100644 src_features/generic_tx_parser/calldata.c create mode 100644 src_features/generic_tx_parser/calldata.h diff --git a/src_features/generic_tx_parser/calldata.c b/src_features/generic_tx_parser/calldata.c new file mode 100644 index 000000000..0b152f70f --- /dev/null +++ b/src_features/generic_tx_parser/calldata.c @@ -0,0 +1,65 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +#include +#include "calldata.h" +#include "os_print.h" +#include "mem.h" + +typedef struct { + uint8_t *ptr; + size_t size; + size_t expected_size; +} s_calldata; + +static s_calldata g_calldata = {0}; + +bool calldata_init(size_t size) { + if ((g_calldata.ptr = mem_alloc(size)) == NULL) { + return false; + } + g_calldata.expected_size = size; + return true; +} + +bool calldata_append(const uint8_t *buffer, size_t size) { + if ((g_calldata.size + size) > g_calldata.expected_size) { + return false; + } + memcpy(&g_calldata.ptr[g_calldata.size], buffer, size); + g_calldata.size += size; + return true; +} + +void calldata_cleanup(void) { + mem_dealloc(g_calldata.size); + explicit_bzero(&g_calldata, sizeof(g_calldata)); +} + +static bool has_valid_calldata(const s_calldata *calldata) { + if (calldata->ptr == NULL) { + PRINTF("Error: no calldata!\n"); + return false; + } + if (g_calldata.size != g_calldata.expected_size) { + PRINTF("Error: incomplete calldata!\n"); + return false; + } + return true; +} + +const uint8_t *calldata_get_selector(void) { + if (!has_valid_calldata(&g_calldata) || (g_calldata.size < CALLDATA_SELECTOR_SIZE)) { + return NULL; + } + return g_calldata.ptr; +} + +const uint8_t *calldata_get_chunk(int idx) { + if (!has_valid_calldata(&g_calldata) || + (g_calldata.size < (CALLDATA_SELECTOR_SIZE + ((size_t) idx + 1) * CALLDATA_CHUNK_SIZE))) { + return NULL; + } + return &g_calldata.ptr[CALLDATA_SELECTOR_SIZE + (idx * CALLDATA_CHUNK_SIZE)]; +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/calldata.h b/src_features/generic_tx_parser/calldata.h new file mode 100644 index 000000000..5a71a2b2d --- /dev/null +++ b/src_features/generic_tx_parser/calldata.h @@ -0,0 +1,17 @@ +#ifndef CALLDATA_H_ +#define CALLDATA_H_ + +#include +#include +#include + +#define CALLDATA_SELECTOR_SIZE 4 +#define CALLDATA_CHUNK_SIZE 32 + +bool calldata_init(size_t size); +bool calldata_append(const uint8_t *buffer, size_t size); +void calldata_cleanup(void); +const uint8_t *calldata_get_selector(void); +const uint8_t *calldata_get_chunk(int idx); + +#endif // !CALLDATA_H_ diff --git a/src_features/signTx/ethUstream.c b/src_features/signTx/ethUstream.c index a31dce62a..d0bc6dbaa 100644 --- a/src_features/signTx/ethUstream.c +++ b/src_features/signTx/ethUstream.c @@ -22,6 +22,9 @@ #include "rlp_utils.h" #include "common_utils.h" #include "feature_signTx.h" +#ifdef HAVE_GENERIC_TX_PARSER +#include "calldata.h" +#endif static bool check_fields(txContext_t *context, const char *name, uint32_t length) { UNUSED(name); // Just for the case where DEBUG is not enabled @@ -292,6 +295,16 @@ static bool processData(txContext_t *context) { if (copySize == 1 && *context->workBuffer == 0x00) { context->content->dataPresent = false; } +#ifdef HAVE_GENERIC_TX_PARSER + if (context->store_calldata) { + if (context->currentFieldPos == 0) { + if (!calldata_init(context->currentFieldLength)) { + return false; + } + } + calldata_append(context->workBuffer, copySize); + } +#endif if (copyTxData(context, NULL, copySize) == false) { return false; } From 4656df57a749f66820a5bd7d51b4a8545078fc28 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 22 Oct 2024 10:47:04 +0200 Subject: [PATCH 12/45] Now does not start the flow if in store calldata mode --- src_features/signTx/cmd_signTx.c | 2 +- src_features/signTx/feature_signTx.h | 2 +- src_features/signTx/logic_signTx.c | 37 +++++++++++++++++--------- src_features/signTx/ui_common_signTx.c | 2 +- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src_features/signTx/cmd_signTx.c b/src_features/signTx/cmd_signTx.c index 69fa7b782..8e0b427f5 100644 --- a/src_features/signTx/cmd_signTx.c +++ b/src_features/signTx/cmd_signTx.c @@ -118,7 +118,7 @@ uint16_t handleSign(uint8_t p1, case USTREAM_SUSPENDED: break; case USTREAM_FINISHED: - sw = finalize_parsing(); + sw = finalize_parsing(&txContext); break; case USTREAM_PROCESSING: return APDU_RESPONSE_OK; diff --git a/src_features/signTx/feature_signTx.h b/src_features/signTx/feature_signTx.h index 9deb4173b..48ccdb463 100644 --- a/src_features/signTx/feature_signTx.h +++ b/src_features/signTx/feature_signTx.h @@ -28,7 +28,7 @@ typedef enum { } plugin_ui_state_t; customStatus_e customProcessor(txContext_t *context); -uint16_t finalize_parsing(void); +uint16_t finalize_parsing(const txContext_t *context); void ux_approve_tx(bool fromPlugin); void start_signature_flow(void); diff --git a/src_features/signTx/logic_signTx.c b/src_features/signTx/logic_signTx.c index ad07bc901..16c5dc088 100644 --- a/src_features/signTx/logic_signTx.c +++ b/src_features/signTx/logic_signTx.c @@ -13,6 +13,7 @@ #include "manage_asset_info.h" #include "handle_swap_sign_transaction.h" #include "os_math.h" +#include "calldata.h" static bool g_use_standard_ui; @@ -633,7 +634,7 @@ void start_signature_flow(void) { } } -uint16_t finalize_parsing(void) { +uint16_t finalize_parsing(const txContext_t *context) { uint16_t sw = APDU_RESPONSE_UNKNOWN; g_use_standard_ui = true; @@ -641,20 +642,32 @@ uint16_t finalize_parsing(void) { if (sw != APDU_RESPONSE_OK) { return sw; } - // If called from swap, the user has already validated a standard transaction - // And we have already checked the fields of this transaction above - if (G_called_from_swap && g_use_standard_ui) { - ui_idle(); - io_seproxyhal_touch_tx_ok(); +#ifdef HAVE_GENERIC_TX_PARSER + if (context->store_calldata) { + if (calldata_get_selector() == NULL) { + PRINTF("Asked to store calldata but none was provided!\n"); + return APDU_RESPONSE_INVALID_DATA; + } } else { +#else + (void) context; + { +#endif + // If called from swap, the user has already validated a standard transaction + // And we have already checked the fields of this transaction above + if (G_called_from_swap && g_use_standard_ui) { + ui_idle(); + io_seproxyhal_touch_tx_ok(); + } else { #ifdef HAVE_BAGL - // If blind-signing detected, start the warning flow beforehand - if (tmpContent.txContent.dataPresent) { - ui_warning_blind_signing(); - } else + // If blind-signing detected, start the warning flow beforehand + if (tmpContent.txContent.dataPresent) { + ui_warning_blind_signing(); + } else #endif - { - start_signature_flow(); + { + start_signature_flow(); + } } } return APDU_RESPONSE_OK; diff --git a/src_features/signTx/ui_common_signTx.c b/src_features/signTx/ui_common_signTx.c index 9e8b4713f..71fd16a97 100644 --- a/src_features/signTx/ui_common_signTx.c +++ b/src_features/signTx/ui_common_signTx.c @@ -80,7 +80,7 @@ unsigned int io_seproxyhal_touch_data_ok(void) { case USTREAM_SUSPENDED: break; case USTREAM_FINISHED: - err = finalize_parsing(); + err = finalize_parsing(&txContext); break; case USTREAM_PROCESSING: err = io_seproxyhal_send_status(APDU_RESPONSE_OK, 0, false, true); From 0f3272c8789fdb5aa4e12529f3c89211f3c643b9 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 4 Nov 2024 18:45:49 +0100 Subject: [PATCH 13/45] Added bytearray handling to format_tlv function in Python client --- client/src/ledger_app_clients/ethereum/tlv.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/src/ledger_app_clients/ethereum/tlv.py b/client/src/ledger_app_clients/ethereum/tlv.py index fd5dc109e..e7a5d4a89 100644 --- a/client/src/ledger_app_clients/ethereum/tlv.py +++ b/client/src/ledger_app_clients/ethereum/tlv.py @@ -9,12 +9,14 @@ def der_encode(value: int) -> bytes: return value_bytes -def format_tlv(tag: int, value: Union[int, str, bytes]) -> bytes: +def format_tlv(tag: int, value: Union[int, str, bytes, bytearray]) -> bytes: if isinstance(value, int): # max() to have minimum length of 1 value = value.to_bytes(max(1, (value.bit_length() + 7) // 8), 'big') elif isinstance(value, str): value = value.encode() + elif isinstance(value, bytearray): + value = bytes(value) assert isinstance(value, bytes), f"Unhandled TLV formatting for type : {type(value)}" From b606721041d419270627a4db2e10416a3654b8b9 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 31 Oct 2024 10:34:54 +0100 Subject: [PATCH 14/45] Commonize RLP parsing status handling --- src_features/signTx/cmd_signTx.c | 59 +++++++++++++++++--------- src_features/signTx/feature_signTx.h | 3 ++ src_features/signTx/ui_common_signTx.c | 22 ++-------- 3 files changed, 45 insertions(+), 39 deletions(-) diff --git a/src_features/signTx/cmd_signTx.c b/src_features/signTx/cmd_signTx.c index 8e0b427f5..e8645c68d 100644 --- a/src_features/signTx/cmd_signTx.c +++ b/src_features/signTx/cmd_signTx.c @@ -63,6 +63,36 @@ static uint16_t handle_first_sign_chunk(const uint8_t *payload, return APDU_NO_RESPONSE; } +uint16_t handle_parsing_status(parserStatus_e status) { + uint16_t sw = APDU_NO_RESPONSE; + + switch (status) { + case USTREAM_SUSPENDED: + break; + case USTREAM_FINISHED: + sw = finalize_parsing(&txContext); + break; + case USTREAM_PROCESSING: + sw = APDU_RESPONSE_OK; + break; + case USTREAM_FAULT: + if (G_called_from_swap) { + // We have encountered an error while trying to sign a SWAP type transaction + // Return dedicated error code and flag an early exit back to Exchange + G_swap_response_ready = true; + send_swap_error(ERROR_GENERIC, APP_CODE_CALLDATA_ISSUE, NULL, NULL); + // unreachable + os_sched_exit(0); + } + sw = APDU_RESPONSE_INVALID_DATA; + break; + default: + PRINTF("Unexpected parser status\n"); + sw = APDU_RESPONSE_INVALID_DATA; + } + return sw; +} + uint16_t handleSign(uint8_t p1, uint8_t p2, const uint8_t *payload, @@ -114,27 +144,14 @@ uint16_t handleSign(uint8_t p1, PRINTF("Parser not initialized\n"); return APDU_RESPONSE_CONDITION_NOT_SATISFIED; } - switch (process_tx(&txContext, &payload[offset], length - offset)) { - case USTREAM_SUSPENDED: - break; - case USTREAM_FINISHED: - sw = finalize_parsing(&txContext); - break; - case USTREAM_PROCESSING: - return APDU_RESPONSE_OK; - case USTREAM_FAULT: - if (G_called_from_swap) { - // We have encountered an error while trying to sign a SWAP type transaction - // Return dedicated error code and flag an early exit back to Exchange - G_swap_response_ready = true; - send_swap_error(ERROR_GENERIC, APP_CODE_CALLDATA_ISSUE, NULL, NULL); - // unreachable - os_sched_exit(0); - } - return APDU_RESPONSE_INVALID_DATA; - default: - PRINTF("Unexpected parser status\n"); - return APDU_RESPONSE_INVALID_DATA; + parserStatus_e pstatus = process_tx(&txContext, &payload[offset], length - offset); + sw = handle_parsing_status(pstatus); + if ((pstatus == USTREAM_FINISHED) && (sw == APDU_RESPONSE_OK)) { + // don't respond now, will be done after review + sw = APDU_NO_RESPONSE; + } + if (sw != APDU_NO_RESPONSE) { + return sw; } *flags |= IO_ASYNCH_REPLY; diff --git a/src_features/signTx/feature_signTx.h b/src_features/signTx/feature_signTx.h index 48ccdb463..dd953edac 100644 --- a/src_features/signTx/feature_signTx.h +++ b/src_features/signTx/feature_signTx.h @@ -1,6 +1,7 @@ #ifndef _SIGN_TX_H_ #define _SIGN_TX_H_ +#include #include #include "ethUstream.h" @@ -34,4 +35,6 @@ void start_signature_flow(void); void send_swap_error(uint8_t error_code, app_code_t app_code, const char *str1, const char *str2); +uint16_t handle_parsing_status(parserStatus_e status); + #endif // _SIGN_TX_H_ diff --git a/src_features/signTx/ui_common_signTx.c b/src_features/signTx/ui_common_signTx.c index 71fd16a97..6a6cf7ea2 100644 --- a/src_features/signTx/ui_common_signTx.c +++ b/src_features/signTx/ui_common_signTx.c @@ -73,26 +73,12 @@ unsigned int io_seproxyhal_touch_tx_cancel(void) { } unsigned int io_seproxyhal_touch_data_ok(void) { - parserStatus_e txResult = USTREAM_FINISHED; - txResult = continueTx(&txContext); unsigned int err = 0; - switch (txResult) { - case USTREAM_SUSPENDED: - break; - case USTREAM_FINISHED: - err = finalize_parsing(&txContext); - break; - case USTREAM_PROCESSING: - err = io_seproxyhal_send_status(APDU_RESPONSE_OK, 0, false, true); - break; - case USTREAM_FAULT: - err = io_seproxyhal_send_status(APDU_RESPONSE_INVALID_DATA, 0, true, true); - break; - default: - PRINTF("Unexpected parser status\n"); - err = io_seproxyhal_send_status(APDU_RESPONSE_INVALID_DATA, 0, true, true); + parserStatus_e pstatus = continueTx(&txContext); + uint16_t sw = handle_parsing_status(pstatus); + if ((pstatus != USTREAM_SUSPENDED) && (pstatus != USTREAM_FINISHED)) { + err = io_seproxyhal_send_status(sw, 0, sw != APDU_RESPONSE_OK, true); } - return err; } From 6b3e7eca5439672c8993106bd052fc506a10b555 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 31 Oct 2024 11:51:06 +0100 Subject: [PATCH 15/45] Prevent plugin init when in store mode --- src_features/signTx/logic_signTx.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src_features/signTx/logic_signTx.c b/src_features/signTx/logic_signTx.c index 16c5dc088..649d69159 100644 --- a/src_features/signTx/logic_signTx.c +++ b/src_features/signTx/logic_signTx.c @@ -61,12 +61,18 @@ customStatus_e customProcessor(txContext_t *context) { // If contract debugging mode is activated, do not go through the plugin activation // as they wouldn't be displayed if the plugin consumes all data but fallbacks // Still go through plugin activation in Swap context - if (!N_storage.contractDetails || G_called_from_swap) { - eth_plugin_prepare_init(&pluginInit, - context->workBuffer, - context->currentFieldLength); - dataContext.tokenContext.pluginStatus = - eth_plugin_perform_init(tmpContent.txContent.destination, &pluginInit); +#ifdef HAVE_GENERIC_TX_PARSER + if (!context->store_calldata) { +#else + { +#endif + if (!N_storage.contractDetails || G_called_from_swap) { + eth_plugin_prepare_init(&pluginInit, + context->workBuffer, + context->currentFieldLength); + dataContext.tokenContext.pluginStatus = + eth_plugin_perform_init(tmpContent.txContent.destination, &pluginInit); + } } PRINTF("pluginstatus %d\n", dataContext.tokenContext.pluginStatus); eth_plugin_result_t status = dataContext.tokenContext.pluginStatus; From 397d0e665f18c689793d4181304fd731df1a67e7 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 31 Oct 2024 11:56:02 +0100 Subject: [PATCH 16/45] Fix blind signing warning in store mode --- src_features/signTx/logic_signTx.c | 35 ++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src_features/signTx/logic_signTx.c b/src_features/signTx/logic_signTx.c index 649d69159..3c21a3ccd 100644 --- a/src_features/signTx/logic_signTx.c +++ b/src_features/signTx/logic_signTx.c @@ -94,10 +94,16 @@ customStatus_e customProcessor(txContext_t *context) { uint32_t copySize; uint32_t fieldPos = context->currentFieldPos; if (fieldPos == 0) { // not reached if a plugin is available - if (!N_storage.dataAllowed) { - PRINTF("Data field forbidden\n"); - ui_error_blind_signing(); - return CUSTOM_FAULT; +#ifdef HAVE_GENERIC_TX_PARSER + if (!context->store_calldata) { +#else + { +#endif + if (!N_storage.dataAllowed) { + PRINTF("Data field forbidden\n"); + ui_error_blind_signing(); + return CUSTOM_FAULT; + } } if (!N_storage.contractDetails) { return CUSTOM_NOT_HANDLED; @@ -382,7 +388,7 @@ __attribute__((noreturn)) void send_swap_error(uint8_t error_code, finalize_exchange_sign_transaction(false); } -__attribute__((noinline)) static uint16_t finalize_parsing_helper(void) { +__attribute__((noinline)) static uint16_t finalize_parsing_helper(const txContext_t *context) { char displayBuffer[50]; uint8_t decimals = WEI_TO_ETHER; uint64_t chain_id = get_tx_chain_id(); @@ -529,11 +535,18 @@ __attribute__((noinline)) static uint16_t finalize_parsing_helper(void) { } } - if (tmpContent.txContent.dataPresent && !N_storage.dataAllowed) { - PRINTF("Data is present but not allowed\n"); - report_finalize_error(); - ui_error_blind_signing(); - return false; +#ifdef HAVE_GENERIC_TX_PARSER + if (!context->store_calldata) { +#else + (void) context; + { +#endif + if (tmpContent.txContent.dataPresent && !N_storage.dataAllowed) { + PRINTF("Data is present but not allowed\n"); + report_finalize_error(); + ui_error_blind_signing(); + return false; + } } // Prepare destination address and amount to display @@ -644,7 +657,7 @@ uint16_t finalize_parsing(const txContext_t *context) { uint16_t sw = APDU_RESPONSE_UNKNOWN; g_use_standard_ui = true; - sw = finalize_parsing_helper(); + sw = finalize_parsing_helper(context); if (sw != APDU_RESPONSE_OK) { return sw; } From 41cfa3be048deca057a4f0fe4dfc7e65169f47e5 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 31 Oct 2024 11:56:56 +0100 Subject: [PATCH 17/45] Store mode now sends RAPDU immediately when finished parsing --- src_features/signTx/cmd_signTx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src_features/signTx/cmd_signTx.c b/src_features/signTx/cmd_signTx.c index e8645c68d..df74041aa 100644 --- a/src_features/signTx/cmd_signTx.c +++ b/src_features/signTx/cmd_signTx.c @@ -146,9 +146,11 @@ uint16_t handleSign(uint8_t p1, } parserStatus_e pstatus = process_tx(&txContext, &payload[offset], length - offset); sw = handle_parsing_status(pstatus); - if ((pstatus == USTREAM_FINISHED) && (sw == APDU_RESPONSE_OK)) { - // don't respond now, will be done after review - sw = APDU_NO_RESPONSE; + if (p2 == SIGN_MODE_BASIC) { + if ((pstatus == USTREAM_FINISHED) && (sw == APDU_RESPONSE_OK)) { + // don't respond now, will be done after review + sw = APDU_NO_RESPONSE; + } } if (sw != APDU_NO_RESPONSE) { return sw; From 79f5a6eb7d18e9c244379292bd51013d1297309e Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 3 Dec 2024 18:43:35 +0100 Subject: [PATCH 18/45] New util functions for string and buffer manipulation + SET_BIT macro --- src/utils.c | 31 +++++++++++++++++++++++++++++++ src/utils.h | 11 +++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/utils.c create mode 100644 src/utils.h diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 000000000..012f351ff --- /dev/null +++ b/src/utils.c @@ -0,0 +1,31 @@ +#include +#include "utils.h" + +void buf_shrink_expand(const uint8_t *src, size_t src_size, uint8_t *dst, size_t dst_size) { + size_t src_off; + size_t dst_off; + + if (src_size > dst_size) { + src_off = src_size - dst_size; + dst_off = 0; + } else { + src_off = 0; + dst_off = dst_size - src_size; + explicit_bzero(dst, dst_off); + } + memcpy(&dst[dst_off], &src[src_off], dst_size - dst_off); +} + +void str_cpy_explicit_trunc(const char *src, size_t src_size, char *dst, size_t dst_size) { + size_t off; + const char trunc_marker[] = "..."; + + if (src_size < dst_size) { + memcpy(dst, src, src_size); + dst[src_size] = '\0'; + } else { + off = dst_size - sizeof(trunc_marker); + memcpy(dst, src, off); + memcpy(&dst[off], trunc_marker, sizeof(trunc_marker)); + } +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 000000000..20e2c99b2 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,11 @@ +#ifndef UTILS_H_ +#define UTILS_H_ + +#include + +#define SET_BIT(a) (1 << a) + +void buf_shrink_expand(const uint8_t *src, size_t src_size, uint8_t *dst, size_t dst_size); +void str_cpy_explicit_trunc(const char *src, size_t src_size, char *dst, size_t dst_size); + +#endif // !UTILS_H_ From e2dff527b75596c3257b3a776070c0ee8ccb1a96 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 23 Oct 2024 11:41:14 +0200 Subject: [PATCH 19/45] New non feature-specific TLV parser --- src/tlv.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tlv.h | 24 +++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 src/tlv.c create mode 100644 src/tlv.h diff --git a/src/tlv.c b/src/tlv.c new file mode 100644 index 000000000..7cc1cca87 --- /dev/null +++ b/src/tlv.c @@ -0,0 +1,106 @@ +#include +#include +#include "tlv.h" +#include "os_print.h" // PRINTF +#include "read.h" // read_u32_be + +typedef enum { TLV_STATE_TAG, TLV_STATE_LENGTH, TLV_STATE_VALUE } e_tlv_state; + +#define DER_LONG_FORM_FLAG 0x80 // 8th bit set +#define DER_FIRST_BYTE_VALUE_MASK 0x7f + +static bool parse_der_value(const uint8_t *payload, + uint16_t size, + uint16_t *offset, + uint32_t *value) { + uint8_t byte_length; + uint8_t buf[sizeof(*value)]; + + if (value == NULL) { + return false; + } + if (payload[*offset] & DER_LONG_FORM_FLAG) { // long form + byte_length = payload[*offset] & DER_FIRST_BYTE_VALUE_MASK; + *offset += 1; + if ((*offset + byte_length) > size) { + PRINTF("TLV payload too small for DER encoded value\n"); + return false; + } + if ((byte_length > sizeof(buf)) || (byte_length == 0)) { + PRINTF("Unexpectedly long DER-encoded value (%u bytes)\n", byte_length); + return false; + } + memset(buf, 0, (sizeof(buf) - byte_length)); + memcpy(buf + (sizeof(buf) - byte_length), &payload[*offset], byte_length); + *value = read_u32_be(buf, 0); + *offset += byte_length; + } else { // short form + *value = payload[*offset]; + *offset += 1; + } + return true; +} + +/** + * Parse a TLV payload + * + * @param[in] payload pointer to TLV payload + * @param[in] size size of the given payload + * @param[in] handler function to be called for each TLV data + * @param[in,out] context structure passed to the handler function so the context from one handler + * invocation to the next can be kept + * @return whether the parsing (and handling) was successful or not + */ +bool tlv_parse(const uint8_t *payload, uint16_t size, f_tlv_data_handler handler, void *context) { + e_tlv_state state = TLV_STATE_TAG; + uint16_t offset = 0; + uint32_t tmp_value; + s_tlv_data data; + + while (offset < size) { + switch (state) { + case TLV_STATE_TAG: + data.raw = &payload[offset]; + if (!parse_der_value(payload, size, &offset, &tmp_value) || + (tmp_value > UINT8_MAX)) { + return false; + } + data.tag = (uint8_t) tmp_value; + state = TLV_STATE_LENGTH; + break; + + case TLV_STATE_LENGTH: + if (!parse_der_value(payload, size, &offset, &tmp_value) || + (tmp_value > UINT16_MAX)) { + return false; + } + data.length = (uint16_t) tmp_value; + data.value = &payload[offset]; + state = (data.length > 0) ? TLV_STATE_VALUE : TLV_STATE_TAG; + break; + + case TLV_STATE_VALUE: + if ((offset + data.length) > size) { + PRINTF("Error: value would go beyond the TLV payload!\n"); + return false; + } + offset += data.length; + state = TLV_STATE_TAG; + break; + + default: + return false; + } + + // back to TAG, call the handler function + if ((state == TLV_STATE_TAG) && (handler != NULL)) { + data.raw_size = (&payload[offset] - data.raw); + if (!(*handler)(&data, context)) { + PRINTF("Error: failed in handler of tag 0x%02x\n", data.tag); + return false; + } + } + } + // make sure the payload didn't end prematurely + return state == TLV_STATE_TAG; +} diff --git a/src/tlv.h b/src/tlv.h new file mode 100644 index 000000000..c41d67c66 --- /dev/null +++ b/src/tlv.h @@ -0,0 +1,24 @@ +#ifndef TLV_H_ +#define TLV_H_ + +#include +#include + +#define TLV_TAG_ERROR_MSG "Error: Unknown TLV tag (0x%02x)\n" + +typedef struct { + uint8_t tag; // T + uint16_t length; // L + const uint8_t *value; // V + + // in case the handler needs to do some processing on the raw TLV-encoded data (like computing a + // signature) + const uint8_t *raw; + uint16_t raw_size; +} s_tlv_data; + +typedef bool (*f_tlv_data_handler)(const s_tlv_data *, void *); + +bool tlv_parse(const uint8_t *payload, uint16_t size, f_tlv_data_handler handler, void *context); + +#endif // !TLV_H_ From 118d237503c0990aa464506234df8b640a8d6708 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 6 Dec 2024 14:34:46 +0100 Subject: [PATCH 20/45] util function to extract TLV from an APDU --- src/tlv_apdu.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/tlv_apdu.h | 14 ++++++++ 2 files changed, 104 insertions(+) create mode 100644 src/tlv_apdu.c create mode 100644 src/tlv_apdu.h diff --git a/src/tlv_apdu.c b/src/tlv_apdu.c new file mode 100644 index 000000000..a8bc02f76 --- /dev/null +++ b/src/tlv_apdu.c @@ -0,0 +1,90 @@ +#include +#include "tlv_apdu.h" +#include "read.h" +#ifdef HAVE_DYN_MEM_ALLOC +#include "mem.h" +#endif +#include "os_print.h" + +#ifdef HAVE_DYN_MEM_ALLOC +static uint8_t *g_tlv_payload = NULL; +static uint16_t g_tlv_size; +static uint16_t g_tlv_pos; +static bool g_dyn; +#endif + +bool tlv_from_apdu(bool first_chunk, + uint8_t lc, + const uint8_t *payload, + f_tlv_payload_handler handler) { +#ifndef HAVE_DYN_MEM_ALLOC + uint16_t g_tlv_size = 0; + uint16_t g_tlv_pos = 0; +#endif + bool ret = true; + uint8_t offset = 0; + uint8_t chunk_length; + + if (first_chunk) { + if ((offset + sizeof(g_tlv_size)) > lc) { + return false; + } + g_tlv_size = read_u16_be(payload, offset); + offset += sizeof(g_tlv_size); + g_tlv_pos = 0; + if (g_tlv_size > (lc - offset)) { +#ifdef HAVE_DYN_MEM_ALLOC + if (g_tlv_payload != NULL) { + PRINTF("Error: remnants from an incomplete TLV payload!\n"); + mem_dealloc(g_tlv_size); + g_tlv_payload = NULL; + return false; + } + + g_dyn = true; + g_tlv_payload = mem_alloc(g_tlv_size); + } else { + g_dyn = false; +#else + PRINTF( + "Error: cannot handle a TLV payload accross multiple APDUs without dynamic memory " + "allocator\n"); + return false; +#endif + } + } +#ifdef HAVE_DYN_MEM_ALLOC + if (g_dyn && (g_tlv_payload == NULL)) { + return false; + } +#endif + chunk_length = lc - offset; + if ((g_tlv_pos + chunk_length) > g_tlv_size) { + PRINTF("TLV payload bigger than expected!\n"); +#ifdef HAVE_DYN_MEM_ALLOC + if (g_dyn) { + mem_dealloc(g_tlv_size); + g_tlv_payload = NULL; + } +#endif + return false; + } + +#ifdef HAVE_DYN_MEM_ALLOC + if (g_dyn) { + memcpy(g_tlv_payload + g_tlv_pos, payload + offset, chunk_length); + } +#endif + + g_tlv_pos += chunk_length; + + if (g_tlv_pos == g_tlv_size) { +#ifdef HAVE_DYN_MEM_ALLOC + ret = (*handler)(g_dyn ? g_tlv_payload : &payload[offset], g_tlv_size, g_dyn); + g_tlv_payload = NULL; +#else + ret = (*handler)(&payload[offset], g_tlv_size, false); +#endif + } + return ret; +} diff --git a/src/tlv_apdu.h b/src/tlv_apdu.h new file mode 100644 index 000000000..5c831ef0d --- /dev/null +++ b/src/tlv_apdu.h @@ -0,0 +1,14 @@ +#ifndef TLV_APDU_H_ +#define TLV_APDU_H_ + +#include +#include + +typedef bool (*f_tlv_payload_handler)(const uint8_t *payload, uint16_t size, bool to_free); + +bool tlv_from_apdu(bool first_chunk, + uint8_t lc, + const uint8_t *payload, + f_tlv_payload_handler handler); + +#endif // !TLV_APDU_H_ From 67f6e962b784248169cc0151a0981ad2535ec958 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 6 Dec 2024 18:35:09 +0100 Subject: [PATCH 21/45] Provide enum value APDU implementation --- makefile_conf/features.mk | 1 + src/apdu_constants.h | 1 + src/main.c | 7 + .../provide_enum_value/cmd_enum_value.c | 34 ++++ .../provide_enum_value/cmd_enum_value.h | 8 + src_features/provide_enum_value/enum_value.c | 184 ++++++++++++++++++ src_features/provide_enum_value/enum_value.h | 39 ++++ 7 files changed, 274 insertions(+) create mode 100644 src_features/provide_enum_value/cmd_enum_value.c create mode 100644 src_features/provide_enum_value/cmd_enum_value.h create mode 100644 src_features/provide_enum_value/enum_value.c create mode 100644 src_features/provide_enum_value/enum_value.h diff --git a/makefile_conf/features.mk b/makefile_conf/features.mk index ef5b86bdf..b56dc9fee 100644 --- a/makefile_conf/features.mk +++ b/makefile_conf/features.mk @@ -37,6 +37,7 @@ ifneq ($(TARGET_NAME),TARGET_NANOS) endif ifneq ($(TARGET_NAME),TARGET_NANOS) + DEFINES += HAVE_ENUM_VALUE DEFINES += HAVE_GENERIC_TX_PARSER endif diff --git a/src/apdu_constants.h b/src/apdu_constants.h index d523e8184..a91e26c9b 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -25,6 +25,7 @@ #define INS_EIP712_FILTERING 0x1E #define INS_ENS_GET_CHALLENGE 0x20 #define INS_ENS_PROVIDE_INFO 0x22 +#define INS_PROVIDE_ENUM_VALUE 0x24 #define INS_PROVIDE_NETWORK_CONFIGURATION 0x30 #define P1_CONFIRM 0x01 #define P1_NON_CONFIRM 0x00 diff --git a/src/main.c b/src/main.c index 8706f6bbc..19bd36865 100644 --- a/src/main.c +++ b/src/main.c @@ -39,6 +39,7 @@ #ifdef HAVE_DYN_MEM_ALLOC #include "mem.h" #endif +#include "cmd_enum_value.h" tmpCtx_t tmpCtx; txContext_t txContext; @@ -235,6 +236,12 @@ static uint16_t handleApdu(command_t *cmd, uint32_t *flags, uint32_t *tx) { break; #endif // HAVE_TRUSTED_NAME +#ifdef HAVE_ENUM_VALUE + case INS_PROVIDE_ENUM_VALUE: + sw = handle_enum_value(cmd->p1, cmd->p2, cmd->lc, cmd->data); + break; +#endif // HAVE_ENUM_VALUE + default: sw = APDU_RESPONSE_INVALID_INS; break; diff --git a/src_features/provide_enum_value/cmd_enum_value.c b/src_features/provide_enum_value/cmd_enum_value.c new file mode 100644 index 000000000..33bc68bd8 --- /dev/null +++ b/src_features/provide_enum_value/cmd_enum_value.c @@ -0,0 +1,34 @@ +#ifdef HAVE_ENUM_VALUE + +#include +#include "cmd_enum_value.h" +#include "apdu_constants.h" +#include "read.h" +#include "mem.h" +#include "enum_value.h" +#include "tlv_apdu.h" +#include "tlv.h" + +bool handle_tlv_payload(const uint8_t *payload, uint16_t size, bool to_free) { + s_enum_value_ctx ctx = {0}; + + cx_sha256_init(&ctx.struct_hash); + if (!tlv_parse(payload, size, (f_tlv_data_handler) &handle_enum_value_struct, &ctx)) { + return false; + } + if (to_free) mem_dealloc(sizeof(size)); + if (!verify_enum_value_struct(&ctx)) { + return false; + } + return true; +} + +uint16_t handle_enum_value(uint8_t p1, uint8_t p2, uint8_t lc, const uint8_t *payload) { + (void) p2; + if (!tlv_from_apdu(p1 == P1_FIRST_CHUNK, lc, payload, &handle_tlv_payload)) { + return APDU_RESPONSE_INVALID_DATA; + } + return APDU_RESPONSE_OK; +} + +#endif // HAVE_ENUM_VALUE diff --git a/src_features/provide_enum_value/cmd_enum_value.h b/src_features/provide_enum_value/cmd_enum_value.h new file mode 100644 index 000000000..ea28eb98f --- /dev/null +++ b/src_features/provide_enum_value/cmd_enum_value.h @@ -0,0 +1,8 @@ +#ifndef CMD_ENUM_VALUE_H_ +#define CMD_ENUM_VALUE_H_ + +#include + +uint16_t handle_enum_value(uint8_t p1, uint8_t p2, uint8_t lc, const uint8_t *payload); + +#endif // !CMD_ENUM_VALUE_H_ diff --git a/src_features/provide_enum_value/enum_value.c b/src_features/provide_enum_value/enum_value.c new file mode 100644 index 000000000..64cf2fc30 --- /dev/null +++ b/src_features/provide_enum_value/enum_value.c @@ -0,0 +1,184 @@ +#ifdef HAVE_ENUM_VALUE + +#include +#include "enum_value.h" +#include "read.h" +#include "public_keys.h" +#include "utils.h" + +enum { + TAG_VERSION = 0x00, + TAG_CHAIN_ID = 0x01, + TAG_CONTRACT_ADDR = 0x02, + TAG_SELECTOR = 0x03, + TAG_ID = 0x04, + TAG_VALUE = 0x05, + TAG_NAME = 0x06, + TAG_SIGNATURE = 0xff, +}; + +static s_enum_value_entry g_enum_value = {0}; + +static bool handle_version(const s_tlv_data *data, s_enum_value_ctx *context) { + if (data->length != sizeof(context->enum_value.version)) { + return false; + } + context->enum_value.version = data->value[0]; + return true; +} + +static bool handle_chain_id(const s_tlv_data *data, s_enum_value_ctx *context) { + uint8_t buf[sizeof(context->enum_value.entry.chain_id)]; + + if (data->length > sizeof(buf)) { + return false; + } + buf_shrink_expand(data->value, data->length, buf, sizeof(buf)); + context->enum_value.entry.chain_id = read_u64_be(buf, 0); + return true; +} + +static bool handle_contract_addr(const s_tlv_data *data, s_enum_value_ctx *context) { + if (data->length > sizeof(context->enum_value.entry.contract_addr)) { + return false; + } + buf_shrink_expand(data->value, + data->length, + context->enum_value.entry.contract_addr, + sizeof(context->enum_value.entry.contract_addr)); + return true; +} + +static bool handle_selector(const s_tlv_data *data, s_enum_value_ctx *context) { + if (data->length > sizeof(context->enum_value.entry.selector)) { + return false; + } + buf_shrink_expand(data->value, + data->length, + context->enum_value.entry.selector, + sizeof(context->enum_value.entry.selector)); + return true; +} + +static bool handle_id(const s_tlv_data *data, s_enum_value_ctx *context) { + if (data->length != sizeof(context->enum_value.entry.id)) { + return false; + } + context->enum_value.entry.id = data->value[0]; + return true; +} + +static bool handle_value(const s_tlv_data *data, s_enum_value_ctx *context) { + if (data->length != sizeof(context->enum_value.entry.value)) { + return false; + } + context->enum_value.entry.value = data->value[0]; + return true; +} + +static bool handle_name(const s_tlv_data *data, s_enum_value_ctx *context) { + str_cpy_explicit_trunc((const char *) data->value, + data->length, + context->enum_value.entry.name, + sizeof(context->enum_value.entry.name)); + return true; +} + +static bool handle_signature(const s_tlv_data *data, s_enum_value_ctx *context) { + if (data->length > sizeof(context->enum_value.signature)) { + return false; + } + context->enum_value.signature_length = data->length; + memcpy(context->enum_value.signature, data->value, data->length); + return true; +} + +bool handle_enum_value_struct(const s_tlv_data *data, s_enum_value_ctx *context) { + bool ret; + + switch (data->tag) { + case TAG_VERSION: + ret = handle_version(data, context); + break; + case TAG_CHAIN_ID: + ret = handle_chain_id(data, context); + break; + case TAG_CONTRACT_ADDR: + ret = handle_contract_addr(data, context); + break; + case TAG_SELECTOR: + ret = handle_selector(data, context); + break; + case TAG_ID: + ret = handle_id(data, context); + break; + case TAG_VALUE: + ret = handle_value(data, context); + break; + case TAG_NAME: + ret = handle_name(data, context); + break; + case TAG_SIGNATURE: + ret = handle_signature(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + ret = false; + } + if (ret && (data->tag != TAG_SIGNATURE)) { + if (cx_hash_no_throw((cx_hash_t *) &context->struct_hash, + 0, + data->raw, + data->raw_size, + NULL, + 0) != CX_OK) { + return false; + } + } + return ret; +} + +bool verify_enum_value_struct(const s_enum_value_ctx *context) { + uint8_t hash[INT256_LENGTH]; + + if (cx_hash_no_throw((cx_hash_t *) &context->struct_hash, + CX_LAST, + NULL, + 0, + hash, + sizeof(hash)) != CX_OK) { + PRINTF("Could not finalize struct hash!\n"); + return false; + } + // TODO: change to LEDGER_CALLDATA_DESCRIPTOR key once available + if (check_signature_with_pubkey("enum value", + hash, + sizeof(hash), + LEDGER_SIGNATURE_PUBLIC_KEY, + sizeof(LEDGER_SIGNATURE_PUBLIC_KEY), +#ifdef HAVE_LEDGER_PKI + CERTIFICATE_PUBLIC_KEY_USAGE_COIN_META, +#endif + (uint8_t *) context->enum_value.signature, + context->enum_value.signature_length) != CX_OK) { + return false; + } + memcpy(&g_enum_value, &context->enum_value.entry, sizeof(g_enum_value)); + return true; +} + +const char *get_matching_enum_name(const uint64_t *chain_id, + const uint8_t *contract_addr, + const uint8_t *selector, + uint8_t id, + uint8_t value) { + if ((*chain_id == g_enum_value.chain_id) && + (memcmp(contract_addr, g_enum_value.contract_addr, ADDRESS_LENGTH) == 0) && + (memcmp(selector, g_enum_value.selector, SELECTOR_SIZE) == 0) && (id == g_enum_value.id) && + (value == g_enum_value.value)) { + return g_enum_value.name; + } + return NULL; +} + +#endif // HAVE_ENUM_VALUE diff --git a/src_features/provide_enum_value/enum_value.h b/src_features/provide_enum_value/enum_value.h new file mode 100644 index 000000000..0ce6a64c2 --- /dev/null +++ b/src_features/provide_enum_value/enum_value.h @@ -0,0 +1,39 @@ +#ifndef ENUM_VALUE_H_ +#define ENUM_VALUE_H_ + +#include +#include "common_utils.h" // ADDRESS_LENGTH +#include "plugin_utils.h" // SELECTOR_SIZE +#include "tlv.h" +#include "cx.h" + +typedef struct { + uint64_t chain_id; + uint8_t contract_addr[ADDRESS_LENGTH]; + uint8_t selector[SELECTOR_SIZE]; + uint8_t id; + uint8_t value; + char name[21]; +} s_enum_value_entry; + +typedef struct { + uint8_t version; + s_enum_value_entry entry; + uint8_t signature_length; + uint8_t signature[73]; +} s_enum_value; + +typedef struct { + s_enum_value enum_value; + cx_sha256_t struct_hash; +} s_enum_value_ctx; + +bool handle_enum_value_struct(const s_tlv_data *data, s_enum_value_ctx *context); +bool verify_enum_value_struct(const s_enum_value_ctx *context); +const char *get_matching_enum_name(const uint64_t *chain_id, + const uint8_t *contract_addr, + const uint8_t *selector, + uint8_t id, + uint8_t value); + +#endif // !ENUM_VALUE_H_ From c8379050658444e9e24b27d22b5c5189b2f621bf Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 Dec 2024 15:41:51 +0100 Subject: [PATCH 22/45] Added provide_enum_value APDU in Python client --- .../src/ledger_app_clients/ethereum/client.py | 6 ++ .../ethereum/command_builder.py | 20 ++++++- .../ledger_app_clients/ethereum/enum_value.py | 59 +++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 client/src/ledger_app_clients/ethereum/enum_value.py diff --git a/client/src/ledger_app_clients/ethereum/client.py b/client/src/ledger_app_clients/ethereum/client.py index 8fdc693af..e6611dee9 100644 --- a/client/src/ledger_app_clients/ethereum/client.py +++ b/client/src/ledger_app_clients/ethereum/client.py @@ -530,3 +530,9 @@ def provide_network_information(self, assert response.status == StatusWord.OK response = self._exchange(chunks[-1]) assert response.status == StatusWord.OK + + def provide_enum_value(self, payload: bytes) -> RAPDU: + chunks = self._cmd_builder.provide_enum_value(payload) + for chunk in chunks[:-1]: + self._exchange(chunk) + return self._exchange(chunks[-1]) diff --git a/client/src/ledger_app_clients/ethereum/command_builder.py b/client/src/ledger_app_clients/ethereum/command_builder.py index c43fe9462..240b8edea 100644 --- a/client/src/ledger_app_clients/ethereum/command_builder.py +++ b/client/src/ledger_app_clients/ethereum/command_builder.py @@ -15,6 +15,7 @@ class InsType(IntEnum): SIGN = 0x04 PERSONAL_SIGN = 0x08 PROVIDE_ERC20_TOKEN_INFORMATION = 0x0a + EXTERNAL_PLUGIN_SETUP = 0x12 PROVIDE_NFT_INFORMATION = 0x14 SET_PLUGIN = 0x16 PERFORM_PRIVACY_OPERATION = 0x18 @@ -24,7 +25,7 @@ class InsType(IntEnum): EIP712_SIGN = 0x0c GET_CHALLENGE = 0x20 PROVIDE_TRUSTED_NAME = 0x22 - EXTERNAL_PLUGIN_SETUP = 0x12 + PROVIDE_ENUM_VALUE = 0x24 PROVIDE_NETWORK_INFORMATION = 0x30 @@ -427,3 +428,20 @@ def provide_network_information(self, icon = icon[0xff:] p1 = P1Type.FOLLOWING_CHUNK return chunks + + def common_tlv_serialize(self, tlv_payload: bytes, ins: InsType) -> list[bytes]: + chunks = list() + payload = struct.pack(">H", len(tlv_payload)) + payload += tlv_payload + p1 = 1 + while len(payload) > 0: + chunks.append(self._serialize(ins, + p1, + 0x00, + payload[:0xff])) + payload = payload[0xff:] + p1 = 0 + return chunks + + def provide_enum_value(self, tlv_payload: bytes) -> list[bytes]: + return self.common_tlv_serialize(tlv_payload, InsType.PROVIDE_ENUM_VALUE) diff --git a/client/src/ledger_app_clients/ethereum/enum_value.py b/client/src/ledger_app_clients/ethereum/enum_value.py new file mode 100644 index 000000000..84febcfa2 --- /dev/null +++ b/client/src/ledger_app_clients/ethereum/enum_value.py @@ -0,0 +1,59 @@ +from enum import IntEnum +from typing import Optional +from .tlv import format_tlv +from .keychain import sign_data, Key + + +class Tag(IntEnum): + VERSION = 0x00 + CHAIN_ID = 0x01 + CONTRACT_ADDR = 0x02 + SELECTOR = 0x03 + ID = 0x04 + VALUE = 0x05 + NAME = 0x06 + SIGNATURE = 0xff + + +class EnumValue: + version: int + chain_id: int + contract_addr: bytes + selector: bytes + id: int + value: int + name: str + signature: Optional[bytes] = None + + def __init__(self, + version: int, + chain_id: int, + contract_addr: bytes, + selector: bytes, + id: int, + value: int, + name: str, + signature: Optional[bytes] = None): + self.version = version + self.chain_id = chain_id + self.contract_addr = contract_addr + self.selector = selector + self.id = id + self.value = value + self.name = name + self.signature = signature + + def serialize(self) -> bytes: + payload = bytearray() + payload += format_tlv(Tag.VERSION, self.version) + payload += format_tlv(Tag.CHAIN_ID, self.chain_id) + payload += format_tlv(Tag.CONTRACT_ADDR, self.contract_addr) + payload += format_tlv(Tag.SELECTOR, self.selector) + payload += format_tlv(Tag.ID, self.id) + payload += format_tlv(Tag.VALUE, self.value) + payload += format_tlv(Tag.NAME, self.name) + sig = self.signature + if sig is None: + sig = sign_data(Key.CAL, payload) + payload += format_tlv(Tag.SIGNATURE, sig) + return payload From e7401010368eeef93989268aeb7d63f22b35025a Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 Dec 2024 15:42:26 +0100 Subject: [PATCH 23/45] GTP APDU handling --- src/apdu_constants.h | 2 ++ src/main.c | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/apdu_constants.h b/src/apdu_constants.h index a91e26c9b..22a4eda3d 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -26,6 +26,8 @@ #define INS_ENS_GET_CHALLENGE 0x20 #define INS_ENS_PROVIDE_INFO 0x22 #define INS_PROVIDE_ENUM_VALUE 0x24 +#define INS_GTP_TRANSACTION_INFO 0x26 +#define INS_GTP_FIELD 0x28 #define INS_PROVIDE_NETWORK_CONFIGURATION 0x30 #define P1_CONFIRM 0x01 #define P1_NON_CONFIRM 0x00 diff --git a/src/main.c b/src/main.c index 19bd36865..636c9a2c3 100644 --- a/src/main.c +++ b/src/main.c @@ -242,6 +242,16 @@ static uint16_t handleApdu(command_t *cmd, uint32_t *flags, uint32_t *tx) { break; #endif // HAVE_ENUM_VALUE +#ifdef HAVE_GENERIC_TX_PARSER + case INS_GTP_TRANSACTION_INFO: + // TODO + break; + + case INS_GTP_FIELD: + // TODO + break; +#endif // HAVE_GENERIC_TX_PARSER + default: sw = APDU_RESPONSE_INVALID_INS; break; From d7cea405af8fe11cc412787d2e5838f9adb50d55 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 26 Nov 2024 14:53:40 +0100 Subject: [PATCH 24/45] Python client can now take an optional mode for the sign operation And removed useless argument from sign functions in client --- client/src/ledger_app_clients/ethereum/client.py | 11 +++++++++-- .../ledger_app_clients/ethereum/command_builder.py | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/client/src/ledger_app_clients/ethereum/client.py b/client/src/ledger_app_clients/ethereum/client.py index e6611dee9..b8a673cef 100644 --- a/client/src/ledger_app_clients/ethereum/client.py +++ b/client/src/ledger_app_clients/ethereum/client.py @@ -77,6 +77,12 @@ class PKIPubKeyUsage(IntEnum): PUBKEY_USAGE_SEED_ID_AUTH = 0x09 +class SignMode(IntEnum): + BASIC = 0x00 + STORE = 0x01 + START_FLOW = 0x02 + + class PKIClient: _CLA: int = 0xB0 _INS: int = 0x06 @@ -211,7 +217,8 @@ def eip712_filtering_raw(self, name: str, sig: bytes, discarded: bool): def sign(self, bip32_path: str, - tx_params: dict): + tx_params: dict, + mode: SignMode = SignMode.BASIC): tx = Web3().eth.account.create().sign_transaction(tx_params).rawTransaction prefix = bytes() suffix = [] @@ -223,7 +230,7 @@ def sign(self, suffix = [int(tx_params["chainId"]), bytes(), bytes()] decoded = rlp.decode(tx)[:-3] # remove already computed signature tx = prefix + rlp.encode(decoded + suffix) - chunks = self._cmd_builder.sign(bip32_path, tx, suffix) + chunks = self._cmd_builder.sign(bip32_path, tx, mode) for chunk in chunks[:-1]: self._exchange(chunk) return self._exchange_async(chunks[-1]) diff --git a/client/src/ledger_app_clients/ethereum/command_builder.py b/client/src/ledger_app_clients/ethereum/command_builder.py index 240b8edea..18a18a47f 100644 --- a/client/src/ledger_app_clients/ethereum/command_builder.py +++ b/client/src/ledger_app_clients/ethereum/command_builder.py @@ -262,7 +262,7 @@ def set_external_plugin(self, plugin_name: str, contract_address: bytes, selecto 0x00, data) - def sign(self, bip32_path: str, rlp_data: bytes, vrs: list) -> list[bytes]: + def sign(self, bip32_path: str, rlp_data: bytes, p2: int) -> list[bytes]: apdus = list() payload = pack_derivation_path(bip32_path) payload += rlp_data @@ -270,7 +270,7 @@ def sign(self, bip32_path: str, rlp_data: bytes, vrs: list) -> list[bytes]: while len(payload) > 0: apdus.append(self._serialize(InsType.SIGN, p1, - 0x00, + p2, payload[:0xff])) payload = payload[0xff:] p1 = P1Type.SIGN_SUBSQT_CHUNK From 60a52c2da32a603cff5bd8d815bdd2e9487d156c Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 Dec 2024 15:49:53 +0100 Subject: [PATCH 25/45] Added transaction info APDU to Python client --- .../src/ledger_app_clients/ethereum/client.py | 6 ++ .../ethereum/command_builder.py | 4 ++ client/src/ledger_app_clients/ethereum/gcs.py | 69 +++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 client/src/ledger_app_clients/ethereum/gcs.py diff --git a/client/src/ledger_app_clients/ethereum/client.py b/client/src/ledger_app_clients/ethereum/client.py index b8a673cef..7ce9008db 100644 --- a/client/src/ledger_app_clients/ethereum/client.py +++ b/client/src/ledger_app_clients/ethereum/client.py @@ -543,3 +543,9 @@ def provide_enum_value(self, payload: bytes) -> RAPDU: for chunk in chunks[:-1]: self._exchange(chunk) return self._exchange(chunks[-1]) + + def provide_transaction_info(self, payload: bytes) -> RAPDU: + chunks = self._cmd_builder.provide_transaction_info(payload) + for chunk in chunks[:-1]: + self._exchange(chunk) + return self._exchange(chunks[-1]) diff --git a/client/src/ledger_app_clients/ethereum/command_builder.py b/client/src/ledger_app_clients/ethereum/command_builder.py index 18a18a47f..090833dd8 100644 --- a/client/src/ledger_app_clients/ethereum/command_builder.py +++ b/client/src/ledger_app_clients/ethereum/command_builder.py @@ -26,6 +26,7 @@ class InsType(IntEnum): GET_CHALLENGE = 0x20 PROVIDE_TRUSTED_NAME = 0x22 PROVIDE_ENUM_VALUE = 0x24 + PROVIDE_TRANSACTION_INFO = 0x26 PROVIDE_NETWORK_INFORMATION = 0x30 @@ -445,3 +446,6 @@ def common_tlv_serialize(self, tlv_payload: bytes, ins: InsType) -> list[bytes]: def provide_enum_value(self, tlv_payload: bytes) -> list[bytes]: return self.common_tlv_serialize(tlv_payload, InsType.PROVIDE_ENUM_VALUE) + + def provide_transaction_info(self, tlv_payload: bytes) -> list[bytes]: + return self.common_tlv_serialize(tlv_payload, InsType.PROVIDE_TRANSACTION_INFO) diff --git a/client/src/ledger_app_clients/ethereum/gcs.py b/client/src/ledger_app_clients/ethereum/gcs.py new file mode 100644 index 000000000..124114ef3 --- /dev/null +++ b/client/src/ledger_app_clients/ethereum/gcs.py @@ -0,0 +1,69 @@ +from typing import Optional + +from .tlv import format_tlv +from .keychain import sign_data, Key + + +class TxInfo(): + version: int + chain_id: int + contract_addr: bytes + selector: bytes + fields_hash: bytes + operation_type: str + creator_name: Optional[str] + creator_legal_name: Optional[str] + creator_url: Optional[str] + contract_name: Optional[str] + deploy_date: Optional[int] + signature: Optional[bytes] + + def __init__(self, + version: int, + chain_id: int, + contract_addr: bytes, + selector: bytes, + fields_hash: bytes, + operation_type: str, + creator_name: Optional[str] = None, + creator_legal_name: Optional[str] = None, + creator_url: Optional[str] = None, + contract_name: Optional[str] = None, + deploy_date: Optional[int] = None, + signature: Optional[bytes] = None): + self.version = version + self.chain_id = chain_id + self.contract_addr = contract_addr + self.selector = selector + self.fields_hash = fields_hash + self.operation_type = operation_type + self.creator_name = creator_name + self.creator_legal_name = creator_legal_name + self.creator_url = creator_url + self.contract_name = contract_name + self.deploy_date = deploy_date + self.signature = signature + + def serialize(self) -> bytes: + payload = bytearray() + payload += format_tlv(0x00, self.version) + payload += format_tlv(0x01, self.chain_id) + payload += format_tlv(0x02, self.contract_addr) + payload += format_tlv(0x03, self.selector) + payload += format_tlv(0x04, self.fields_hash) + payload += format_tlv(0x05, self.operation_type) + if self.creator_name is not None: + payload += format_tlv(0x06, self.creator_name) + if self.creator_legal_name is not None: + payload += format_tlv(0x07, self.creator_legal_name) + if self.creator_url is not None: + payload += format_tlv(0x08, self.creator_url) + if self.contract_name is not None: + payload += format_tlv(0x09, self.contract_name) + if self.deploy_date is not None: + payload += format_tlv(0x0a, self.deploy_date) + signature = self.signature + if signature is None: + signature = sign_data(Key.CAL, payload) + payload += format_tlv(0xff, signature) + return payload From c83d6a915e8b5351b35646541ac7f2dd065c7818 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 3 Dec 2024 09:48:10 +0100 Subject: [PATCH 26/45] Added field descriptor TLV serialization to the Python client --- client/src/ledger_app_clients/ethereum/gcs.py | 408 ++++++++++++++++++ 1 file changed, 408 insertions(+) diff --git a/client/src/ledger_app_clients/ethereum/gcs.py b/client/src/ledger_app_clients/ethereum/gcs.py index 124114ef3..b48fc6f43 100644 --- a/client/src/ledger_app_clients/ethereum/gcs.py +++ b/client/src/ledger_app_clients/ethereum/gcs.py @@ -1,7 +1,10 @@ from typing import Optional +from enum import IntEnum +import struct from .tlv import format_tlv from .keychain import sign_data, Key +from .client import TrustedNameType, TrustedNameSource class TxInfo(): @@ -67,3 +70,408 @@ def serialize(self) -> bytes: signature = sign_data(Key.CAL, payload) payload += format_tlv(0xff, signature) return payload + + +class ParamType(IntEnum): + RAW = 0x00 + AMOUNT = 0x01 + TOKEN_AMOUNT = 0x02 + NFT = 0x03 + DATETIME = 0x04 + DURATION = 0x05 + UNIT = 0x06 + ENUM = 0x07 + TRUSTED_NAME = 0x08 + CALLDATA = 0x09 + + +class TypeFamily(IntEnum): + UINT = 0x01 + INT = 0x02 + UFIXED = 0x03 + FIXED = 0x04 + ADDRESS = 0x05 + BOOL = 0x06 + BYTES = 0x07 + STRING = 0x08 + + +class PathTuple: + value: int + + def __init__(self, value: int): + self.value = value + + def serialize(self) -> bytes: + return struct.pack(">H", self.value) + + +class PathArray: + weight: int + start: Optional[int] + end: Optional[int] + + def __init__(self, + weight: int = 1, + start: Optional[int] = None, + end: Optional[int] = None): + self.weight = weight + self.start = start + self.end = end + + def serialize(self) -> bytes: + payload = bytearray() + payload += format_tlv(0x01, self.weight) + if self.start is not None: + payload += format_tlv(0x02, struct.pack(">h", self.start)) + if self.end is not None: + payload += format_tlv(0x03, struct.pack(">h", self.end)) + return payload + + +class PathRef: + def __init__(self): + pass + + def serialize(self) -> bytes: + return bytes() + + +class PathLeafType(IntEnum): + ARRAY = 0x01 + TUPLE = 0x02 + STATIC = 0x03 + DYNAMIC = 0x04 + + +class PathLeaf: + type: PathLeafType + + def __init__(self, type: PathLeafType): + self.type = type + + def serialize(self) -> bytes: + return struct.pack("B", self.type) + + +class PathSlice: + start: Optional[int] + end: Optional[int] + + def __init__(self, start: Optional[int] = None, end: Optional[int] = None): + self.start = start + self.end = end + + def serialize(self) -> bytes: + payload = bytearray() + if self.start is not None: + payload += format_tlv(0x01, struct.pack(">h", self.start)) + if self.end is not None: + payload += format_tlv(0x02, struct.pack(">h", self.end)) + return payload + + +PathElement = PathTuple | PathArray | PathRef | PathLeaf | PathSlice + + +class DataPath: + version: int + path: list[PathElement] + + def __init__(self, version: int, path: list[PathElement]): + self.version = version + self.path = path + + def serialize(self) -> bytes: + payload = bytearray() + payload += format_tlv(0x00, self.version) + for node in self.path: + if isinstance(node, PathTuple): + tag = 0x01 + elif isinstance(node, PathArray): + tag = 0x02 + elif isinstance(node, PathRef): + tag = 0x03 + elif isinstance(node, PathLeaf): + tag = 0x04 + elif isinstance(node, PathSlice): + tag = 0x05 + else: + assert False, f"Unknown path node type : {type(node)}" + payload += format_tlv(tag, node.serialize()) + return payload + + +class ContainerPath(IntEnum): + FROM = 0x00 + TO = 0x01 + VALUE = 0x02 + + +class Value: + version: int + type_family: TypeFamily + type_size: Optional[int] + data_path: Optional[DataPath] + container_path: Optional[ContainerPath] + constant: Optional[bytes] + + def __init__(self, + version: int, + type_family: TypeFamily, + type_size: Optional[int] = None, + data_path: Optional[DataPath] = None, + container_path: Optional[ContainerPath] = None, + constant: Optional[bytes] = None): + self.version = version + self.type_family = type_family + self.type_size = type_size + self.data_path = data_path + self.container_path = container_path + self.constant = constant + + def serialize(self) -> bytes: + payload = bytearray() + payload += format_tlv(0x00, self.version) + payload += format_tlv(0x01, self.type_family) + if self.type_size is not None: + payload += format_tlv(0x02, self.type_size) + if self.data_path is not None: + payload += format_tlv(0x03, self.data_path.serialize()) + if self.container_path is not None: + payload += format_tlv(0x04, self.container_path) + if self.constant is not None: + payload += format_tlv(0x05, self.constant) + return payload + + +class ParamRaw: + version: int + value: Value + + def __init__(self, version: int, value: Value): + self.version = version + self.value = value + + def serialize(self) -> bytes: + payload = bytearray() + payload += format_tlv(0x00, self.version) + payload += format_tlv(0x01, self.value.serialize()) + return payload + + +class ParamAmount: + version: int + value: Value + + def __init__(self, version: int, value: Value): + self.version = version + self.value = value + + def serialize(self) -> bytes: + payload = bytearray() + payload += format_tlv(0x00, self.version) + payload += format_tlv(0x01, self.value.serialize()) + return payload + + +class ParamTokenAmount: + version: int + value: Value + token: Optional[Value] + native_currency: Optional[list[bytes]] + threshold: Optional[int] + above_threshold_msg: Optional[str] + + def __init__(self, + version: int, + value: Value, + token: Optional[Value] = None, + native_currency: Optional[list[bytes]] = None, + threshold: Optional[int] = None, + above_threshold_msg: Optional[str] = None): + self.version = version + self.value = value + self.token = token + self.native_currency = native_currency + self.threshold = threshold + self.above_threshold_msg = above_threshold_msg + + def serialize(self) -> bytes: + payload = bytearray() + payload += format_tlv(0x00, self.version) + payload += format_tlv(0x01, self.value.serialize()) + if self.token is not None: + payload += format_tlv(0x02, self.token.serialize()) + if self.native_currency is not None: + for nat_cur in self.native_currency: + payload += format_tlv(0x03, nat_cur) + if self.threshold is not None: + payload += format_tlv(0x04, self.threshold) + if self.above_threshold_msg is not None: + payload += format_tlv(0x05, self.above_threshold_msg) + return payload + + +class ParamNFT(): + version: int + id: Value + collection: Value + + def __init__(self, version: int, id: Value, collection: Value): + self.version = version + self.id = id + self.collection = collection + + def serialize(self) -> bytes: + payload = bytearray() + payload += format_tlv(0x00, self.version) + payload += format_tlv(0x01, self.id.serialize()) + payload += format_tlv(0x02, self.collection.serialize()) + return payload + + +class DatetimeType(IntEnum): + DT_UNIX = 0x00 + DT_BLOCKHEIGHT = 0x01 + + +class ParamDatetime(): + version: int + value: Value + type: DatetimeType + + def __init__(self, version: int, value: Value, type: DatetimeType): + self.version = version + self.value = value + self.type = type + + def serialize(self) -> bytes: + payload = bytearray() + payload += format_tlv(0x00, self.version) + payload += format_tlv(0x01, self.value.serialize()) + payload += format_tlv(0x02, self.type) + return payload + + +class ParamDuration(): + version: int + value: Value + + def __init__(self, version: int, value: Value): + self.version = version + self.value = value + + def serialize(self) -> bytes: + payload = bytearray() + payload += format_tlv(0x00, self.version) + payload += format_tlv(0x01, self.value.serialize()) + return payload + + +class ParamUnit(): + version: int + value: Value + base: str + decimals: Optional[int] + prefix: Optional[bool] + + def __init__(self, + version: int, + value: Value, + base: str, + decimals: Optional[int] = None, + prefix: Optional[bool] = None): + self.version = version + self.value = value + self.base = base + self.decimals = decimals + self.prefix = prefix + + def serialize(self) -> bytes: + payload = bytearray() + payload += format_tlv(0x00, self.version) + payload += format_tlv(0x01, self.value.serialize()) + payload += format_tlv(0x02, self.base) + if self.decimals is not None: + payload += format_tlv(0x03, self.decimals) + if self.prefix is not None: + payload += format_tlv(0x04, self.prefix) + return payload + + +class ParamTrustedName(): + version: int + value: Value + types: list[TrustedNameType] + sources: list[TrustedNameSource] + + def __init__(self, version: int, value: Value, types: list[TrustedNameType], sources: list[TrustedNameSource]): + self.version = version + self.value = value + self.types = types + self.sources = sources + + def serialize(self) -> bytes: + payload = bytearray() + payload += format_tlv(0x00, self.version) + payload += format_tlv(0x01, self.value.serialize()) + types = bytearray() + for type in self.types: + types.append(type) + payload += format_tlv(0x02, types) + sources = bytearray() + for source in self.sources: + sources.append(source) + payload += format_tlv(0x03, sources) + return payload + + +class ParamEnum(): + version: int + id: int + value: Value + + def __init__(self, version: int, id: int, value: Value): + self.version = version + self.id = id + self.value = value + + def serialize(self) -> bytes: + payload = bytearray() + payload += format_tlv(0x00, self.version) + payload += format_tlv(0x01, self.id) + payload += format_tlv(0x02, self.value.serialize()) + return payload + + +ParamUnion = ParamRaw | \ + ParamAmount | \ + ParamTokenAmount | \ + ParamNFT | \ + ParamDatetime | \ + ParamDuration | \ + ParamUnit | \ + ParamTrustedName | \ + ParamEnum + + +class Field: + version: int + name: str + param_type: ParamType + param: ParamUnion + + def __init__(self, version: int, name: str, param_type: ParamType, param: ParamUnion): + self.version = version + self.name = name + self.param_type = param_type + self.param = param + + def serialize(self) -> bytes: + payload = bytearray() + payload += format_tlv(0x00, self.version) + payload += format_tlv(0x01, self.name) + payload += format_tlv(0x02, self.param_type) + payload += format_tlv(0x03, self.param.serialize()) + return payload From 613bceba58f3a059591e771b7a2bbc9e29b05a71 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 16 Dec 2024 10:57:44 +0100 Subject: [PATCH 27/45] Added new send_raw_async function to Python client --- client/src/ledger_app_clients/ethereum/client.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/client/src/ledger_app_clients/ethereum/client.py b/client/src/ledger_app_clients/ethereum/client.py index 7ce9008db..a26404d74 100644 --- a/client/src/ledger_app_clients/ethereum/client.py +++ b/client/src/ledger_app_clients/ethereum/client.py @@ -136,6 +136,15 @@ def send_raw(self, cla: int, ins: int, p1: int, p2: int, payload: bytes): header.append(len(payload)) return self._exchange(header + payload) + def send_raw_async(self, cla: int, ins: int, p1: int, p2: int, payload: bytes): + header = bytearray() + header.append(cla) + header.append(ins) + header.append(p1) + header.append(p2) + header.append(len(payload)) + return self._exchange_async(header + payload) + def eip712_send_struct_def_struct_name(self, name: str): return self._exchange_async(self._cmd_builder.eip712_send_struct_def_struct_name(name)) From 4d35cd1bbf9e1c95f7dc41c7bdcf531b07921f5a Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 Dec 2024 15:45:18 +0100 Subject: [PATCH 28/45] TX info handler --- src/main.c | 8 +- src_features/generic_tx_parser/cmd_tx_info.c | 67 ++++ src_features/generic_tx_parser/cmd_tx_info.h | 9 + src_features/generic_tx_parser/gtp_tx_info.c | 359 +++++++++++++++++++ src_features/generic_tx_parser/gtp_tx_info.h | 49 +++ src_features/signTx/cmd_signTx.c | 8 +- 6 files changed, 498 insertions(+), 2 deletions(-) create mode 100644 src_features/generic_tx_parser/cmd_tx_info.c create mode 100644 src_features/generic_tx_parser/cmd_tx_info.h create mode 100644 src_features/generic_tx_parser/gtp_tx_info.c create mode 100644 src_features/generic_tx_parser/gtp_tx_info.h diff --git a/src/main.c b/src/main.c index 636c9a2c3..8459fb7c8 100644 --- a/src/main.c +++ b/src/main.c @@ -40,6 +40,7 @@ #include "mem.h" #endif #include "cmd_enum_value.h" +#include "cmd_tx_info.h" tmpCtx_t tmpCtx; txContext_t txContext; @@ -77,6 +78,11 @@ void reset_app_context() { #endif memset((uint8_t *) &tmpCtx, 0, sizeof(tmpCtx)); forget_known_assets(); +#ifdef HAVE_GENERIC_TX_PARSER + if (txContext.store_calldata) { + gcs_cleanup(); + } +#endif memset((uint8_t *) &txContext, 0, sizeof(txContext)); memset((uint8_t *) &tmpContent, 0, sizeof(tmpContent)); } @@ -244,7 +250,7 @@ static uint16_t handleApdu(command_t *cmd, uint32_t *flags, uint32_t *tx) { #ifdef HAVE_GENERIC_TX_PARSER case INS_GTP_TRANSACTION_INFO: - // TODO + sw = handle_tx_info(cmd->p1, cmd->p2, cmd->lc, cmd->data); break; case INS_GTP_FIELD: diff --git a/src_features/generic_tx_parser/cmd_tx_info.c b/src_features/generic_tx_parser/cmd_tx_info.c new file mode 100644 index 000000000..ab24cefcd --- /dev/null +++ b/src_features/generic_tx_parser/cmd_tx_info.c @@ -0,0 +1,67 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +#include +#include "cmd_tx_info.h" +#include "cx.h" +#include "apdu_constants.h" +#include "mem.h" +#include "gtp_tx_info.h" +#include "tlv.h" +#include "tlv_apdu.h" +#include "calldata.h" + +extern cx_sha3_t hash_ctx; + +static bool handle_tlv_payload(const uint8_t *payload, uint16_t size, bool to_free) { + s_tx_info_ctx ctx = {0}; + + ctx.tx_info = g_tx_info; + explicit_bzero(ctx.tx_info, sizeof(*ctx.tx_info)); + cx_sha256_init((cx_sha256_t *) &ctx.struct_hash); + if (!tlv_parse(payload, size, (f_tlv_data_handler) &handle_tx_info_struct, &ctx)) return false; + if (to_free) mem_dealloc(sizeof(size)); + if (!verify_tx_info_struct(&ctx)) { + return false; + } + if (cx_sha3_init_no_throw(&hash_ctx, 256) != CX_OK) { + return false; + } + return true; +} + +uint16_t handle_tx_info(uint8_t p1, uint8_t p2, uint8_t lc, const uint8_t *payload) { + (void) p2; + if (appState != APP_STATE_SIGNING_TX) { + PRINTF("App not in TX signing mode!\n"); + return APDU_RESPONSE_CONDITION_NOT_SATISFIED; + } + + if (p1 == P1_FIRST_CHUNK) { + if (g_tx_info != NULL) { + PRINTF("Error: TX info received when one was still in RAM!\n"); + gcs_cleanup(); + return APDU_RESPONSE_CONDITION_NOT_SATISFIED; + } + + if ((g_tx_info = mem_alloc(sizeof(*g_tx_info))) == NULL) { + return APDU_RESPONSE_INSUFFICIENT_MEMORY; + } + } + if (g_tx_info == NULL) { + return APDU_RESPONSE_INVALID_DATA; + } + if (!tlv_from_apdu(p1 == P1_FIRST_CHUNK, lc, payload, &handle_tlv_payload)) { + return APDU_RESPONSE_INVALID_DATA; + } + return APDU_RESPONSE_OK; +} + +void gcs_cleanup(void) { + if (g_tx_info != NULL) { + mem_dealloc(sizeof(*g_tx_info)); + g_tx_info = NULL; + } + calldata_cleanup(); +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/cmd_tx_info.h b/src_features/generic_tx_parser/cmd_tx_info.h new file mode 100644 index 000000000..a093669b4 --- /dev/null +++ b/src_features/generic_tx_parser/cmd_tx_info.h @@ -0,0 +1,9 @@ +#ifndef CMD_TX_INFO_H_ +#define CMD_TX_INFO_H_ + +#include + +uint16_t handle_tx_info(uint8_t p1, uint8_t p2, uint8_t lc, const uint8_t *payload); +void gcs_cleanup(void); + +#endif // !CMD_TX_INFO_H_ diff --git a/src_features/generic_tx_parser/gtp_tx_info.c b/src_features/generic_tx_parser/gtp_tx_info.c new file mode 100644 index 000000000..7913ab08d --- /dev/null +++ b/src_features/generic_tx_parser/gtp_tx_info.c @@ -0,0 +1,359 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +#include +#include "gtp_tx_info.h" +#include "read.h" +#include "hash_bytes.h" +#include "network.h" // get_tx_chain_id +#include "shared_context.h" // txContext +#include "utils.h" +#include "time_format.h" +#include "calldata.h" +#include "public_keys.h" // LEDGER_SIGNATURE_PUBLIC_KEY + +enum { + BIT_VERSION = 0, + BIT_CHAIN_ID, + BIT_CONTRACT_ADDR, + BIT_SELECTOR, + BIT_FIELDS_HASH, + BIT_OPERATION_TYPE, + BIT_CREATOR_NAME, + BIT_CREATOR_LEGAL_NAME, + BIT_CREATOR_URL, + BIT_CONTRACT_NAME, + BIT_DEPLOY_DATE, + BIT_SIGNATURE, +}; + +enum { + TAG_VERSION = 0x00, + TAG_CHAIN_ID = 0x01, + TAG_CONTRACT_ADDR = 0x02, + TAG_SELECTOR = 0x03, + TAG_FIELDS_HASH = 0x04, + TAG_OPERATION_TYPE = 0x05, + TAG_CREATOR_NAME = 0x06, + TAG_CREATOR_LEGAL_NAME = 0x07, + TAG_CREATOR_URL = 0x08, + TAG_CONTRACT_NAME = 0x09, + TAG_DEPLOY_DATE = 0x0a, + TAG_SIGNATURE = 0xff, +}; + +s_tx_info *g_tx_info = NULL; +cx_sha3_t hash_ctx; + +static bool handle_version(const s_tlv_data *data, s_tx_info_ctx *context) { + if (data->length != sizeof(context->tx_info->version)) { + return false; + } + context->tx_info->version = data->value[0]; + context->set_flags |= SET_BIT(BIT_VERSION); + return true; +} + +static bool handle_chain_id(const s_tlv_data *data, s_tx_info_ctx *context) { + uint64_t chain_id; + uint8_t buf[sizeof(chain_id)]; + + if (data->length > sizeof(buf)) { + return false; + } + buf_shrink_expand(data->value, data->length, buf, sizeof(buf)); + chain_id = read_u64_be(buf, 0); + if (chain_id != get_tx_chain_id()) { + PRINTF("Error: chain ID mismatch!\n"); + return false; + } + context->tx_info->chain_id = chain_id; + context->set_flags |= SET_BIT(BIT_CHAIN_ID); + return true; +} + +static bool handle_contract_addr(const s_tlv_data *data, s_tx_info_ctx *context) { + uint8_t buf[ADDRESS_LENGTH]; + + if (data->length > sizeof(buf)) { + return false; + } + buf_shrink_expand(data->value, data->length, buf, sizeof(buf)); + if (memcmp(buf, txContext.content->destination, sizeof(buf)) != 0) { + PRINTF("Error: contract address mismatch!\n"); + return false; + } + memcpy(context->tx_info->contract_addr, buf, sizeof(buf)); + context->set_flags |= SET_BIT(BIT_CONTRACT_ADDR); + return true; +} + +static bool handle_selector(const s_tlv_data *data, s_tx_info_ctx *context) { + uint8_t buf[CALLDATA_SELECTOR_SIZE]; + const uint8_t *selector; + + if (data->length > sizeof(buf)) { + return false; + } + buf_shrink_expand(data->value, data->length, buf, sizeof(buf)); + if ((selector = calldata_get_selector()) == NULL) { + return false; + } + if (memcmp(selector, buf, sizeof(buf)) != 0) { + PRINTF("Error: selector mismatch!\n"); + return false; + } + memcpy(context->tx_info->selector, buf, sizeof(buf)); + context->set_flags |= SET_BIT(BIT_SELECTOR); + return true; +} + +static bool handle_fields_hash(const s_tlv_data *data, s_tx_info_ctx *context) { + if (data->length > sizeof(context->tx_info->fields_hash)) { + return false; + } + buf_shrink_expand(data->value, + data->length, + context->tx_info->fields_hash, + sizeof(context->tx_info->fields_hash)); + context->set_flags |= SET_BIT(BIT_FIELDS_HASH); + return true; +} + +static bool handle_operation_type(const s_tlv_data *data, s_tx_info_ctx *context) { + str_cpy_explicit_trunc((const char *) data->value, + data->length, + context->tx_info->operation_type, + sizeof(context->tx_info->operation_type)); + context->set_flags |= SET_BIT(BIT_OPERATION_TYPE); + return true; +} + +static bool handle_creator_name(const s_tlv_data *data, s_tx_info_ctx *context) { + str_cpy_explicit_trunc((const char *) data->value, + data->length, + context->tx_info->creator_name, + sizeof(context->tx_info->creator_name)); + context->set_flags |= SET_BIT(BIT_CREATOR_NAME); + return true; +} + +static bool handle_creator_legal_name(const s_tlv_data *data, s_tx_info_ctx *context) { + str_cpy_explicit_trunc((const char *) data->value, + data->length, + context->tx_info->creator_legal_name, + sizeof(context->tx_info->creator_legal_name)); + context->set_flags |= SET_BIT(BIT_CREATOR_LEGAL_NAME); + return true; +} + +static bool handle_creator_url(const s_tlv_data *data, s_tx_info_ctx *context) { + str_cpy_explicit_trunc((const char *) data->value, + data->length, + context->tx_info->creator_url, + sizeof(context->tx_info->creator_url)); + context->set_flags |= SET_BIT(BIT_CREATOR_URL); + return true; +} + +static bool handle_contract_name(const s_tlv_data *data, s_tx_info_ctx *context) { + str_cpy_explicit_trunc((const char *) data->value, + data->length, + context->tx_info->contract_name, + sizeof(context->tx_info->contract_name)); + context->set_flags |= SET_BIT(BIT_CONTRACT_NAME); + return true; +} + +static bool handle_deploy_date(const s_tlv_data *data, s_tx_info_ctx *context) { + uint8_t buf[sizeof(uint32_t)]; + time_t timestamp; + + if (data->length > sizeof(buf)) { + return false; + } + buf_shrink_expand(data->value, data->length, buf, sizeof(buf)); + timestamp = read_u32_be(buf, 0); + if (!time_format_to_yyyymmdd(×tamp, + context->tx_info->deploy_date, + sizeof(context->tx_info->deploy_date))) { + return false; + } + context->set_flags |= SET_BIT(BIT_DEPLOY_DATE); + return true; +} + +static bool handle_signature(const s_tlv_data *data, s_tx_info_ctx *context) { + if (data->length > sizeof(context->tx_info->signature)) { + return false; + } + memcpy(context->tx_info->signature, data->value, data->length); + context->tx_info->signature_len = data->length; + context->set_flags |= SET_BIT(BIT_SIGNATURE); + return true; +} + +bool handle_tx_info_struct(const s_tlv_data *data, s_tx_info_ctx *context) { + bool ret; + + switch (data->tag) { + case TAG_VERSION: + ret = handle_version(data, context); + break; + case TAG_CHAIN_ID: + ret = handle_chain_id(data, context); + break; + case TAG_CONTRACT_ADDR: + ret = handle_contract_addr(data, context); + break; + case TAG_SELECTOR: + ret = handle_selector(data, context); + break; + case TAG_FIELDS_HASH: + ret = handle_fields_hash(data, context); + break; + case TAG_OPERATION_TYPE: + ret = handle_operation_type(data, context); + break; + case TAG_CREATOR_NAME: + ret = handle_creator_name(data, context); + break; + case TAG_CREATOR_LEGAL_NAME: + ret = handle_creator_legal_name(data, context); + break; + case TAG_CREATOR_URL: + ret = handle_creator_url(data, context); + break; + case TAG_CONTRACT_NAME: + ret = handle_contract_name(data, context); + break; + case TAG_DEPLOY_DATE: + ret = handle_deploy_date(data, context); + break; + case TAG_SIGNATURE: + ret = handle_signature(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + ret = false; + } + if (ret && (data->tag != TAG_SIGNATURE)) { + hash_nbytes(data->raw, data->raw_size, (cx_hash_t *) &context->struct_hash); + } + return ret; +} + +bool verify_tx_info_struct(const s_tx_info_ctx *context) { + uint16_t required_bits = 0; + uint8_t hash[INT256_LENGTH]; + + // check if struct version was provided + required_bits |= SET_BIT(BIT_VERSION); + if ((context->set_flags & required_bits) != required_bits) { + PRINTF("Error: no struct version specified!\n"); + return false; + } + + // verify required fields + switch (context->tx_info->version) { + case 1: + required_bits |= + (SET_BIT(BIT_CHAIN_ID) | SET_BIT(BIT_CONTRACT_ADDR) | SET_BIT(BIT_SELECTOR) | + SET_BIT(BIT_FIELDS_HASH) | SET_BIT(BIT_OPERATION_TYPE) | SET_BIT(BIT_SIGNATURE)); + break; + default: + PRINTF("Error: unsupported tx info version (%u)\n", context->tx_info->version); + return false; + } + + if ((context->set_flags & required_bits) != required_bits) { + PRINTF("Error: missing required field(s)\n"); + return false; + } + + // verify signature + if (cx_hash_no_throw((cx_hash_t *) &context->struct_hash, + CX_LAST, + NULL, + 0, + hash, + sizeof(hash)) != CX_OK) { + PRINTF("Could not finalize struct hash!\n"); + return false; + } + + // TODO: change to LEDGER_CALLDATA_DESCRIPTOR key once available + if (check_signature_with_pubkey("TX info", + hash, + sizeof(hash), + LEDGER_SIGNATURE_PUBLIC_KEY, + sizeof(LEDGER_SIGNATURE_PUBLIC_KEY), +#ifdef HAVE_LEDGER_PKI + CERTIFICATE_PUBLIC_KEY_USAGE_COIN_META, +#endif + (uint8_t *) context->tx_info->signature, + context->tx_info->signature_len) != CX_OK) { + return false; + } + return true; +} + +const char *get_operation_type(void) { + if (g_tx_info->operation_type[0] == '\0') { + return NULL; + } + return g_tx_info->operation_type; +} + +const char *get_creator_name(void) { + if (g_tx_info->creator_name[0] == '\0') { + return NULL; + } + return g_tx_info->creator_name; +} + +const char *get_creator_legal_name(void) { + if (g_tx_info->creator_legal_name[0] == '\0') { + return NULL; + } + return g_tx_info->creator_legal_name; +} + +const char *get_creator_url(void) { + if (g_tx_info->creator_url[0] == '\0') { + return NULL; + } + return g_tx_info->creator_url; +} + +const char *get_contract_name(void) { + if (g_tx_info->contract_name[0] == '\0') { + return NULL; + } + return g_tx_info->contract_name; +} + +const uint8_t *get_contract_addr(void) { + return g_tx_info->contract_addr; +} + +const char *get_deploy_date(void) { + if (g_tx_info->deploy_date[0] == '\0') { + return NULL; + } + return g_tx_info->deploy_date; +} + +cx_hash_t *get_fields_hash_ctx(void) { + return (cx_hash_t *) &hash_ctx; +} + +bool validate_instruction_hash(void) { + uint8_t hash[sizeof(g_tx_info->fields_hash)]; + + if (cx_hash_no_throw((cx_hash_t *) &hash_ctx, CX_LAST, NULL, 0, hash, sizeof(hash)) != CX_OK) { + return false; + } + return memcmp(g_tx_info->fields_hash, hash, sizeof(hash)) == 0; +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/gtp_tx_info.h b/src_features/generic_tx_parser/gtp_tx_info.h new file mode 100644 index 000000000..443cfb374 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_tx_info.h @@ -0,0 +1,49 @@ +#ifndef GTP_TX_INFO_H_ +#define GTP_TX_INFO_H_ + +#include +#include +#include "cx.h" +#include "common_utils.h" // ADDRESS_LENGTH, INT256_LENGTH +#include "calldata.h" +#include "tlv.h" + +typedef struct { + uint8_t version; + uint64_t chain_id; + uint8_t contract_addr[ADDRESS_LENGTH]; + uint8_t selector[CALLDATA_SELECTOR_SIZE]; + uint8_t fields_hash[INT256_LENGTH]; + char operation_type[31]; + char creator_name[23]; + char creator_legal_name[31]; + char creator_url[27]; + char contract_name[31]; + // YYYY-MM-DD\0 + char deploy_date[4 + 1 + 2 + 1 + 2 + 1]; + uint8_t signature_len; + uint8_t signature[73]; +} s_tx_info; + +typedef struct { + cx_sha256_t struct_hash; + uint16_t set_flags; + s_tx_info *tx_info; +} s_tx_info_ctx; + +extern s_tx_info *g_tx_info; + +bool handle_tx_info_struct(const s_tlv_data *data, s_tx_info_ctx *context); +bool verify_tx_info_struct(const s_tx_info_ctx *context); + +const char *get_operation_type(void); +const char *get_creator_name(void); +const char *get_creator_legal_name(void); +const char *get_creator_url(void); +const char *get_contract_name(void); +const uint8_t *get_contract_addr(void); +const char *get_deploy_date(void); +cx_hash_t *get_fields_hash_ctx(void); +bool validate_instruction_hash(void); + +#endif // !GTP_TX_INFO_H_ diff --git a/src_features/signTx/cmd_signTx.c b/src_features/signTx/cmd_signTx.c index df74041aa..1c19c58ad 100644 --- a/src_features/signTx/cmd_signTx.c +++ b/src_features/signTx/cmd_signTx.c @@ -3,6 +3,9 @@ #include "feature_signTx.h" #include "eth_plugin_interface.h" #include "apdu_constants.h" +#ifdef HAVE_GENERIC_TX_PARSER +#include "gtp_tx_info.h" +#endif typedef enum { SIGN_MODE_BASIC = 0, @@ -132,7 +135,10 @@ uint16_t handleSign(uint8_t p1, if (length != 0) { return APDU_RESPONSE_INVALID_DATA; } - // TODO: check hash + if (!validate_instruction_hash()) { + PRINTF("Error: instructions hash mismatch!\n"); + return APDU_RESPONSE_INVALID_DATA; + } *flags |= IO_ASYNCH_REPLY; return APDU_NO_RESPONSE; #endif From 2221ba18bca44d82c0ac31e7fc593840e07236b4 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 18 Dec 2024 15:01:47 +0100 Subject: [PATCH 29/45] GTP Field table implementation --- src_features/generic_tx_parser/cmd_tx_info.c | 3 + .../generic_tx_parser/gtp_field_table.c | 111 ++++++++++++++++++ .../generic_tx_parser/gtp_field_table.h | 19 +++ 3 files changed, 133 insertions(+) create mode 100644 src_features/generic_tx_parser/gtp_field_table.c create mode 100644 src_features/generic_tx_parser/gtp_field_table.h diff --git a/src_features/generic_tx_parser/cmd_tx_info.c b/src_features/generic_tx_parser/cmd_tx_info.c index ab24cefcd..76b017207 100644 --- a/src_features/generic_tx_parser/cmd_tx_info.c +++ b/src_features/generic_tx_parser/cmd_tx_info.c @@ -9,6 +9,7 @@ #include "tlv.h" #include "tlv_apdu.h" #include "calldata.h" +#include "gtp_field_table.h" extern cx_sha3_t hash_ctx; @@ -26,6 +27,7 @@ static bool handle_tlv_payload(const uint8_t *payload, uint16_t size, bool to_fr if (cx_sha3_init_no_throw(&hash_ctx, 256) != CX_OK) { return false; } + field_table_init(); return true; } @@ -57,6 +59,7 @@ uint16_t handle_tx_info(uint8_t p1, uint8_t p2, uint8_t lc, const uint8_t *paylo } void gcs_cleanup(void) { + field_table_cleanup(); if (g_tx_info != NULL) { mem_dealloc(sizeof(*g_tx_info)); g_tx_info = NULL; diff --git a/src_features/generic_tx_parser/gtp_field_table.c b/src_features/generic_tx_parser/gtp_field_table.c new file mode 100644 index 000000000..f8cdf570b --- /dev/null +++ b/src_features/generic_tx_parser/gtp_field_table.c @@ -0,0 +1,111 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +#include +#include "os_print.h" +#include "gtp_field_table.h" +#include "mem.h" + +// type (1) | key_length (1) | value_length (2) | key ($key_length) | value ($value_length) + +typedef struct { + const uint8_t *start; + size_t size; +} s_field_table; + +static s_field_table g_table; + +void field_table_init(void) { + explicit_bzero(&g_table, sizeof(g_table)); +} + +static const uint8_t *get_next_table_entry(const uint8_t *ptr, s_field_table_entry *entry) { + uint8_t key_len; + uint16_t value_len; + + if (ptr == NULL) { + return NULL; + } + if (entry != NULL) { + memcpy(&entry->type, ptr, sizeof(entry->type)); + } + ptr += sizeof(entry->type); + memcpy(&key_len, ptr, sizeof(key_len)); + ptr += sizeof(key_len); + memcpy(&value_len, ptr, sizeof(value_len)); + ptr += sizeof(value_len); + if (entry != NULL) { + entry->key = (char *) ptr; + } + ptr += key_len; + if (entry != NULL) { + entry->value = (char *) ptr; + } + ptr += value_len; + return ptr; +} + +// after this function, field_table_init() will have to be called before using any other field_table +// function +void field_table_cleanup(void) { + const uint8_t *ptr = g_table.start; + + if (ptr != NULL) { + for (size_t i = 0; i < g_table.size; ++i) { + ptr = get_next_table_entry(ptr, NULL); + } + mem_dealloc(ptr - g_table.start); + g_table.start = NULL; + } +} + +bool add_to_field_table(uint8_t type, const char *key, const char *value) { + int offset = 0; + uint8_t *ptr; + uint8_t key_len = strlen(key) + 1; + uint16_t value_len = strlen(value) + 1; + + if ((key == NULL) || (value == NULL)) { + PRINTF("Error: NULL key/value!\n"); + return false; + } + PRINTF(">>> \"%s\": \"%s\"\n", key, value); + if ((ptr = mem_alloc(sizeof(type) + sizeof(uint8_t) + sizeof(uint16_t) + key_len + + value_len)) == NULL) { + return false; + } + if ((g_table.start == NULL) && (g_table.size == 0)) { + g_table.start = ptr; + } + memcpy(&ptr[offset], &type, sizeof(type)); + offset += sizeof(type); + memcpy(&ptr[offset], &key_len, sizeof(key_len)); + offset += sizeof(key_len); + memcpy(&ptr[offset], &value_len, sizeof(value_len)); + offset += sizeof(value_len); + memcpy(&ptr[offset], key, key_len); + offset += key_len; + memcpy(&ptr[offset], value, value_len); + g_table.size += 1; + return true; +} + +size_t field_table_size(void) { + return g_table.size; +} + +bool get_from_field_table(int index, s_field_table_entry *entry) { + const uint8_t *ptr = g_table.start; + + if ((size_t) index >= g_table.size) { + return false; + } + if (ptr == NULL) { + return false; + } + for (int i = 0; i <= index; ++i) { + ptr = get_next_table_entry(ptr, entry); + } + return true; +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/gtp_field_table.h b/src_features/generic_tx_parser/gtp_field_table.h new file mode 100644 index 000000000..bfa1cde34 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_field_table.h @@ -0,0 +1,19 @@ +#ifndef GTP_FIELD_TABLE_H_ +#define GTP_FIELD_TABLE_H_ + +#include +#include + +typedef struct { + uint8_t type; + const char *key; + const char *value; +} s_field_table_entry; + +void field_table_init(void); +void field_table_cleanup(void); +bool add_to_field_table(uint8_t type, const char *key, const char *value); +size_t field_table_size(void); +bool get_from_field_table(int index, s_field_table_entry *entry); + +#endif // !GTP_FIELD_TABLE_H_ From 3c10eeebfa4b5c9d87b2aea8695f0052b44d9f18 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 15 Nov 2024 18:12:28 +0100 Subject: [PATCH 30/45] Added empty UI implementations --- src/common_ui.h | 3 +++ src_bagl/ui_gcs.c | 9 +++++++++ src_features/generic_tx_parser/cmd_tx_info.c | 3 +++ src_features/signTx/cmd_signTx.c | 4 ++++ src_nbgl/ui_gcs.c | 9 +++++++++ 5 files changed, 28 insertions(+) create mode 100644 src_bagl/ui_gcs.c create mode 100644 src_nbgl/ui_gcs.c diff --git a/src/common_ui.h b/src/common_ui.h index e29874bbb..06ffc5d10 100644 --- a/src/common_ui.h +++ b/src/common_ui.h @@ -29,6 +29,9 @@ void ui_712_start_unfiltered(void); void ui_712_switch_to_message(void); void ui_712_switch_to_sign(void); +// Generic clear-signing +bool ui_gcs(void); + #include "ui_callbacks.h" #include diff --git a/src_bagl/ui_gcs.c b/src_bagl/ui_gcs.c new file mode 100644 index 000000000..61f2deac6 --- /dev/null +++ b/src_bagl/ui_gcs.c @@ -0,0 +1,9 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +void ui_gcs(void) { +} + +void ui_gcs_cleanup(void) { +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/cmd_tx_info.c b/src_features/generic_tx_parser/cmd_tx_info.c index 76b017207..c5379523c 100644 --- a/src_features/generic_tx_parser/cmd_tx_info.c +++ b/src_features/generic_tx_parser/cmd_tx_info.c @@ -58,7 +58,10 @@ uint16_t handle_tx_info(uint8_t p1, uint8_t p2, uint8_t lc, const uint8_t *paylo return APDU_RESPONSE_OK; } +void ui_gcs_cleanup(void); + void gcs_cleanup(void) { + ui_gcs_cleanup(); field_table_cleanup(); if (g_tx_info != NULL) { mem_dealloc(sizeof(*g_tx_info)); diff --git a/src_features/signTx/cmd_signTx.c b/src_features/signTx/cmd_signTx.c index 1c19c58ad..0bece7eb7 100644 --- a/src_features/signTx/cmd_signTx.c +++ b/src_features/signTx/cmd_signTx.c @@ -6,6 +6,7 @@ #ifdef HAVE_GENERIC_TX_PARSER #include "gtp_tx_info.h" #endif +#include "common_ui.h" typedef enum { SIGN_MODE_BASIC = 0, @@ -139,6 +140,9 @@ uint16_t handleSign(uint8_t p1, PRINTF("Error: instructions hash mismatch!\n"); return APDU_RESPONSE_INVALID_DATA; } + if (!ui_gcs()) { + return APDU_RESPONSE_INTERNAL_ERROR; + } *flags |= IO_ASYNCH_REPLY; return APDU_NO_RESPONSE; #endif diff --git a/src_nbgl/ui_gcs.c b/src_nbgl/ui_gcs.c new file mode 100644 index 000000000..61f2deac6 --- /dev/null +++ b/src_nbgl/ui_gcs.c @@ -0,0 +1,9 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +void ui_gcs(void) { +} + +void ui_gcs_cleanup(void) { +} + +#endif // HAVE_GENERIC_TX_PARSER From 4cf52d5d9870bb6609ad6b625444e54c22bbf38f Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 19 Nov 2024 17:39:36 +0100 Subject: [PATCH 31/45] get_public_key function is now usable from outside the signTx feature --- src_features/signTx/feature_signTx.h | 2 ++ src_features/signTx/logic_signTx.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src_features/signTx/feature_signTx.h b/src_features/signTx/feature_signTx.h index dd953edac..9f38e1e37 100644 --- a/src_features/signTx/feature_signTx.h +++ b/src_features/signTx/feature_signTx.h @@ -37,4 +37,6 @@ void send_swap_error(uint8_t error_code, app_code_t app_code, const char *str1, uint16_t handle_parsing_status(parserStatus_e status); +uint16_t get_public_key(uint8_t *out, uint8_t outLength); + #endif // _SIGN_TX_H_ diff --git a/src_features/signTx/logic_signTx.c b/src_features/signTx/logic_signTx.c index 3c21a3ccd..740bdbbfb 100644 --- a/src_features/signTx/logic_signTx.c +++ b/src_features/signTx/logic_signTx.c @@ -303,7 +303,7 @@ static uint16_t get_network_as_string(char *out, size_t out_size) { return APDU_RESPONSE_OK; } -static uint16_t get_public_key(uint8_t *out, uint8_t outLength) { +uint16_t get_public_key(uint8_t *out, uint8_t outLength) { uint8_t raw_pubkey[65]; cx_err_t error = CX_INTERNAL_ERROR; From 7b42507fc9f921c21931c134a40ec7c0d7f3b22f Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 18 Dec 2024 11:26:42 +0100 Subject: [PATCH 32/45] GTP field support + param_raw --- src/main.c | 3 +- src_features/generic_tx_parser/cmd_field.c | 51 +++ src_features/generic_tx_parser/cmd_field.h | 8 + .../generic_tx_parser/gtp_data_path.c | 311 ++++++++++++++++++ .../generic_tx_parser/gtp_data_path.h | 60 ++++ src_features/generic_tx_parser/gtp_field.c | 151 +++++++++ src_features/generic_tx_parser/gtp_field.h | 31 ++ .../generic_tx_parser/gtp_field_table.c | 2 +- .../generic_tx_parser/gtp_field_table.h | 5 +- .../generic_tx_parser/gtp_param_raw.c | 188 +++++++++++ .../generic_tx_parser/gtp_param_raw.h | 21 ++ .../generic_tx_parser/gtp_parsed_value.h | 18 + .../generic_tx_parser/gtp_path_array.c | 62 ++++ .../generic_tx_parser/gtp_path_array.h | 22 ++ .../generic_tx_parser/gtp_path_slice.c | 47 +++ .../generic_tx_parser/gtp_path_slice.h | 21 ++ src_features/generic_tx_parser/gtp_value.c | 162 +++++++++ src_features/generic_tx_parser/gtp_value.h | 57 ++++ 18 files changed, 1216 insertions(+), 4 deletions(-) create mode 100644 src_features/generic_tx_parser/cmd_field.c create mode 100644 src_features/generic_tx_parser/cmd_field.h create mode 100644 src_features/generic_tx_parser/gtp_data_path.c create mode 100644 src_features/generic_tx_parser/gtp_data_path.h create mode 100644 src_features/generic_tx_parser/gtp_field.c create mode 100644 src_features/generic_tx_parser/gtp_field.h create mode 100644 src_features/generic_tx_parser/gtp_param_raw.c create mode 100644 src_features/generic_tx_parser/gtp_param_raw.h create mode 100644 src_features/generic_tx_parser/gtp_parsed_value.h create mode 100644 src_features/generic_tx_parser/gtp_path_array.c create mode 100644 src_features/generic_tx_parser/gtp_path_array.h create mode 100644 src_features/generic_tx_parser/gtp_path_slice.c create mode 100644 src_features/generic_tx_parser/gtp_path_slice.h create mode 100644 src_features/generic_tx_parser/gtp_value.c create mode 100644 src_features/generic_tx_parser/gtp_value.h diff --git a/src/main.c b/src/main.c index 8459fb7c8..3e4890801 100644 --- a/src/main.c +++ b/src/main.c @@ -41,6 +41,7 @@ #endif #include "cmd_enum_value.h" #include "cmd_tx_info.h" +#include "cmd_field.h" tmpCtx_t tmpCtx; txContext_t txContext; @@ -254,7 +255,7 @@ static uint16_t handleApdu(command_t *cmd, uint32_t *flags, uint32_t *tx) { break; case INS_GTP_FIELD: - // TODO + sw = handle_field(cmd->p1, cmd->p2, cmd->lc, cmd->data); break; #endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/cmd_field.c b/src_features/generic_tx_parser/cmd_field.c new file mode 100644 index 000000000..ff404e991 --- /dev/null +++ b/src_features/generic_tx_parser/cmd_field.c @@ -0,0 +1,51 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +#include "cmd_field.h" +#include "cx.h" +#include "apdu_constants.h" +#include "mem.h" +#include "tlv.h" +#include "tlv_apdu.h" +#include "gtp_field.h" +#include "cmd_tx_info.h" +#include "gtp_tx_info.h" + +static bool handle_tlv_payload(const uint8_t *payload, uint16_t size, bool to_free) { + s_field field = {0}; + s_field_ctx ctx = {0}; + + ctx.field = &field; + if (!tlv_parse(payload, size, (f_tlv_data_handler) &handle_field_struct, &ctx)) return false; + if (cx_hash_no_throw(get_fields_hash_ctx(), 0, payload, size, NULL, 0) != CX_OK) return false; + if (to_free) mem_dealloc(size); + if (!verify_field_struct(&ctx)) { + PRINTF("Error: could not verify the field struct!\n"); + return false; + } + if (!format_field(&field)) { + PRINTF("Error while formatting the field\n"); + return false; + } + return true; +} + +uint16_t handle_field(uint8_t p1, uint8_t p2, uint8_t lc, const uint8_t *payload) { + (void) p2; + if (appState != APP_STATE_SIGNING_TX) { + PRINTF("App not in TX signing mode!\n"); + return APDU_RESPONSE_CONDITION_NOT_SATISFIED; + } + + if (g_tx_info == NULL) { + PRINTF("Error: Field received without a TX info!\n"); + gcs_cleanup(); + return APDU_RESPONSE_CONDITION_NOT_SATISFIED; + } + + if (!tlv_from_apdu(p1 == P1_FIRST_CHUNK, lc, payload, &handle_tlv_payload)) { + return APDU_RESPONSE_INVALID_DATA; + } + return APDU_RESPONSE_OK; +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/cmd_field.h b/src_features/generic_tx_parser/cmd_field.h new file mode 100644 index 000000000..e246b9eb1 --- /dev/null +++ b/src_features/generic_tx_parser/cmd_field.h @@ -0,0 +1,8 @@ +#ifndef CMD_FIELD_H_ +#define CMD_FIELD_H_ + +#include + +uint16_t handle_field(uint8_t p1, uint8_t p2, uint8_t lc, const uint8_t *payload); + +#endif // !CMD_FIELD_H_ diff --git a/src_features/generic_tx_parser/gtp_data_path.c b/src_features/generic_tx_parser/gtp_data_path.c new file mode 100644 index 000000000..9749f0618 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_data_path.c @@ -0,0 +1,311 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +#include // memcpy / explicit_bzero +#include "os_print.h" +#include "gtp_data_path.h" +#include "read.h" +#include "utils.h" +#include "calldata.h" + +enum { + TAG_VERSION = 0x00, + TAG_TUPLE = 0x01, + TAG_ARRAY = 0x02, + TAG_REF = 0x03, + TAG_LEAF = 0x04, + TAG_SLICE = 0x05, +}; + +static bool handle_version(const s_tlv_data *data, s_data_path_context *context) { + if (data->length != sizeof(context->data_path->version)) { + return false; + } + context->data_path->version = data->value[0]; + return true; +} + +static bool handle_tuple(const s_tlv_data *data, s_data_path_context *context) { + uint8_t buf[sizeof(uint16_t)]; + + if (data->length > sizeof(buf)) { + return false; + } + buf_shrink_expand(data->value, data->length, buf, sizeof(buf)); + context->data_path->elements[context->data_path->size].type = ELEMENT_TYPE_TUPLE; + context->data_path->elements[context->data_path->size].tuple.value = read_u16_be(buf, 0); + + context->data_path->size += 1; + return true; +} + +static bool handle_array(const s_tlv_data *data, s_data_path_context *context) { + s_path_array_context ctx = {0}; + + ctx.args = &context->data_path->elements[context->data_path->size].array; + explicit_bzero(ctx.args, sizeof(*ctx.args)); + if (!tlv_parse(data->value, data->length, (f_tlv_data_handler) &handle_array_struct, &ctx)) + return false; + context->data_path->elements[context->data_path->size].type = ELEMENT_TYPE_ARRAY; + context->data_path->size += 1; + return true; +} + +static bool handle_ref(const s_tlv_data *data, s_data_path_context *context) { + if (data->length != 0) { + return false; + } + context->data_path->elements[context->data_path->size].type = ELEMENT_TYPE_REF; + + context->data_path->size += 1; + return true; +} + +static bool handle_leaf(const s_tlv_data *data, s_data_path_context *context) { + if (data->length != sizeof(e_path_leaf_type)) { + return false; + } + context->data_path->elements[context->data_path->size].type = ELEMENT_TYPE_LEAF; + context->data_path->elements[context->data_path->size].leaf.type = data->value[0]; + + context->data_path->size += 1; + return true; +} + +static bool handle_slice(const s_tlv_data *data, s_data_path_context *context) { + s_path_slice_context ctx = {0}; + + ctx.args = &context->data_path->elements[context->data_path->size].slice; + explicit_bzero(ctx.args, sizeof(*ctx.args)); + if (!tlv_parse(data->value, data->length, (f_tlv_data_handler) &handle_slice_struct, &ctx)) + return false; + context->data_path->elements[context->data_path->size].type = ELEMENT_TYPE_SLICE; + context->data_path->size += 1; + return true; +} + +bool handle_data_path_struct(const s_tlv_data *data, s_data_path_context *context) { + bool ret; + + if (data->tag != TAG_VERSION) { + if (context->data_path->size >= PATH_MAX_SIZE) { + return false; + } + } + switch (data->tag) { + case TAG_VERSION: + ret = handle_version(data, context); + break; + case TAG_TUPLE: + ret = handle_tuple(data, context); + break; + case TAG_ARRAY: + ret = handle_array(data, context); + break; + case TAG_REF: + ret = handle_ref(data, context); + break; + case TAG_LEAF: + ret = handle_leaf(data, context); + break; + case TAG_SLICE: + ret = handle_slice(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + ret = false; + } + return ret; +} + +static bool path_tuple(const s_tuple_args *tuple, uint32_t *offset, uint32_t *ref_offset) { + *ref_offset = *offset; + *offset += tuple->value; + return true; +} + +static bool path_ref(uint32_t *offset, uint32_t *ref_offset) { + uint8_t buf[sizeof(uint16_t)]; + const uint8_t *chunk; + + if ((chunk = calldata_get_chunk(*offset)) == NULL) { + return false; + } + buf_shrink_expand(chunk, CALLDATA_CHUNK_SIZE, buf, sizeof(buf)); + *offset = read_u16_be(buf, 0) / CALLDATA_CHUNK_SIZE; + *offset += *ref_offset; + return true; +} + +static bool path_leaf(const s_leaf_args *leaf, + uint32_t *offset, + s_parsed_value_collection *collection) { + uint8_t buf[sizeof(uint16_t)]; + const uint8_t *chunk; + + if (collection->size > MAX_VALUE_COLLECTION_SIZE) { + return false; + } + + switch (leaf->type) { + case LEAF_TYPE_STATIC: + collection->value[collection->size].length = CALLDATA_CHUNK_SIZE; + break; + + case LEAF_TYPE_DYNAMIC: + if ((chunk = calldata_get_chunk(*offset)) == NULL) { + return false; + } + // TODO: properly handle multi-chunk dynamic values once calldata compression + // is implemented + buf_shrink_expand(chunk, CALLDATA_CHUNK_SIZE, buf, sizeof(buf)); + collection->value[collection->size].length = read_u16_be(buf, 0); + *offset += 1; + break; + + default: + return false; + } + if ((chunk = calldata_get_chunk(*offset)) == NULL) { + return false; + } + collection->value[collection->size].ptr = chunk; + collection->size += 1; + return true; +} + +static bool path_slice(const s_slice_args *slice, s_parsed_value_collection *collection) { + uint16_t start; + uint16_t end; + uint16_t value_length; + + if (collection->size == 0) { + return false; + } + + value_length = collection->value[collection->size - 1].length; + if (slice->has_start) { + start = (slice->start < 0) ? ((int16_t) value_length + slice->start) : slice->start; + } else { + start = 0; + } + + if (slice->has_end) { + end = (slice->end < 0) ? ((int16_t) value_length + slice->end) : slice->end; + } else { + end = value_length; + } + + if ((start >= end) || (end > value_length)) { + return false; + } + collection->value[collection->size - 1].ptr += start; + collection->value[collection->size - 1].length = (end - start); + return true; +} + +#define MAX_ARRAYS 8 + +typedef struct { + uint8_t depth; + uint8_t passes_remaining[MAX_ARRAYS]; + uint8_t index; +} s_arrays_info; + +static bool path_array(const s_array_args *array, + uint32_t *offset, + uint32_t *ref_offset, + s_arrays_info *arrays_info) { + uint8_t buf[sizeof(uint16_t)]; + uint16_t array_size; + uint16_t idx; + uint16_t start; + uint16_t end; + const uint8_t *chunk; + + if (arrays_info->index >= MAX_ARRAYS) { + return false; + } + if ((chunk = calldata_get_chunk(*offset)) == NULL) { + return false; + } + buf_shrink_expand(chunk, CALLDATA_CHUNK_SIZE, buf, sizeof(buf)); + array_size = read_u16_be(buf, 0); + + if (array->has_start) { + start = (array->start < 0) ? ((int16_t) array_size + array->start) : array->start; + } else { + start = 0; + } + + if (array->has_end) { + end = (array->end < 0) ? ((int16_t) array_size + array->end) : array->end; + } else { + end = array_size; + } + + *offset += 1; + if (arrays_info->index == arrays_info->depth) { + // new depth + arrays_info->passes_remaining[arrays_info->index] = (end - start); + arrays_info->depth += 1; + } + idx = start + ((end - start) - arrays_info->passes_remaining[arrays_info->index]); + *ref_offset = *offset; + *offset += (idx * array->weight); + arrays_info->index += 1; + return true; +} + +static void arrays_update(s_arrays_info *arrays_info) { + while (arrays_info->depth > 0) { + if ((arrays_info->passes_remaining[arrays_info->depth - 1] -= 1) > 0) { + break; + } + arrays_info->depth -= 1; + } +} + +bool data_path_get(const s_data_path *data_path, s_parsed_value_collection *collection) { + bool ret; + uint32_t offset; + uint32_t ref_offset; + s_arrays_info arinf = {0}; + + do { + arinf.index = 0; + offset = 0; + ref_offset = offset; + for (int i = 0; i < data_path->size; ++i) { + switch (data_path->elements[i].type) { + case ELEMENT_TYPE_TUPLE: + ret = path_tuple(&data_path->elements[i].tuple, &offset, &ref_offset); + break; + + case ELEMENT_TYPE_ARRAY: + ret = path_array(&data_path->elements[i].array, &offset, &ref_offset, &arinf); + break; + + case ELEMENT_TYPE_REF: + ret = path_ref(&offset, &ref_offset); + break; + + case ELEMENT_TYPE_LEAF: + ret = path_leaf(&data_path->elements[i].leaf, &offset, collection); + break; + + case ELEMENT_TYPE_SLICE: + ret = path_slice(&data_path->elements[i].slice, collection); + break; + + default: + ret = false; + } + + if (!ret) return false; + } + arrays_update(&arinf); + } while (arinf.depth > 0); + return true; +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/gtp_data_path.h b/src_features/generic_tx_parser/gtp_data_path.h new file mode 100644 index 000000000..47f84bc3d --- /dev/null +++ b/src_features/generic_tx_parser/gtp_data_path.h @@ -0,0 +1,60 @@ +#ifndef GTP_DATA_PATH_H_ +#define GTP_DATA_PATH_H_ + +#include +#include +#include +#include "tlv.h" +#include "gtp_parsed_value.h" +#include "gtp_path_array.h" +#include "gtp_path_slice.h" + +#define PATH_MAX_SIZE 16 + +typedef enum { + ELEMENT_TYPE_TUPLE, + ELEMENT_TYPE_ARRAY, + ELEMENT_TYPE_REF, + ELEMENT_TYPE_LEAF, + ELEMENT_TYPE_SLICE, +} e_path_element_type; + +typedef enum { + LEAF_TYPE_ARRAY = 0x01, + LEAF_TYPE_TUPLE = 0x02, + LEAF_TYPE_STATIC = 0x03, + LEAF_TYPE_DYNAMIC = 0x04, +} e_path_leaf_type; + +typedef struct { + uint16_t value; +} s_tuple_args; + +typedef struct { + e_path_leaf_type type; +} s_leaf_args; + +typedef struct { + e_path_element_type type; + union { + s_tuple_args tuple; + s_array_args array; + s_leaf_args leaf; + s_slice_args slice; + }; +} s_path_element; + +typedef struct { + uint8_t version; + uint8_t size; + s_path_element elements[PATH_MAX_SIZE]; +} s_data_path; + +typedef struct { + s_data_path *data_path; +} s_data_path_context; + +bool handle_data_path_struct(const s_tlv_data *data, s_data_path_context *context); +bool data_path_get(const s_data_path *data_path, s_parsed_value_collection *collection); + +#endif // GTP_DATA_PATH_H_ diff --git a/src_features/generic_tx_parser/gtp_field.c b/src_features/generic_tx_parser/gtp_field.c new file mode 100644 index 000000000..3562d7c0d --- /dev/null +++ b/src_features/generic_tx_parser/gtp_field.c @@ -0,0 +1,151 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +#include +#include "gtp_field.h" +#include "utils.h" +#include "os_print.h" + +enum { + BIT_VERSION = 0, + BIT_NAME, + BIT_PARAM_TYPE, + BIT_PARAM, +}; + +enum { + TAG_VERSION = 0x00, + TAG_NAME = 0x01, + PARAM_TYPE = 0x02, + PARAM = 0x03, +}; + +typedef union { + s_param_raw_context raw_ctx; +} u_param_context; + +static bool handle_version(const s_tlv_data *data, s_field_ctx *context) { + if (data->length != sizeof(context->field->version)) { + return false; + } + context->field->version = data->value[0]; + context->set_flags |= SET_BIT(BIT_VERSION); + return true; +} + +static bool handle_name(const s_tlv_data *data, s_field_ctx *context) { + str_cpy_explicit_trunc((const char *) data->value, + data->length, + context->field->name, + sizeof(context->field->name)); + context->set_flags |= SET_BIT(BIT_NAME); + return true; +} + +static bool handle_param_type(const s_tlv_data *data, s_field_ctx *context) { + if (data->length != sizeof(context->field->param_type)) { + return false; + } + if (context->set_flags & SET_BIT(BIT_PARAM_TYPE)) { + PRINTF("Error: More than one PARAM_TYPE in a FIELD struct!\n"); + return false; + } + context->field->param_type = data->value[0]; + switch (context->field->param_type) { + case PARAM_TYPE_RAW: + break; + default: + PRINTF("Error: Unsupported param type (%u)\n", context->field->param_type); + return false; + } + context->set_flags |= SET_BIT(BIT_PARAM_TYPE); + return true; +} + +static bool handle_param(const s_tlv_data *data, s_field_ctx *context) { + f_tlv_data_handler handler; + u_param_context param_ctx = {0}; + + if (context->set_flags & SET_BIT(BIT_PARAM)) { + PRINTF("Error: More than one PARAM in a FIELD struct!\n"); + return false; + } + if (!(context->set_flags & SET_BIT(BIT_PARAM_TYPE))) { + PRINTF("Error: Received PARAM without a previous PARAM_TYPE!\n"); + return false; + } + switch (context->field->param_type) { + case PARAM_TYPE_RAW: + handler = (f_tlv_data_handler) &handle_param_raw_struct; + param_ctx.raw_ctx.param = &context->field->param_raw; + break; + default: + return false; + } + context->set_flags |= SET_BIT(BIT_PARAM); + return tlv_parse(data->value, data->length, handler, ¶m_ctx); +} + +bool handle_field_struct(const s_tlv_data *data, s_field_ctx *context) { + bool ret; + + switch (data->tag) { + case TAG_VERSION: + ret = handle_version(data, context); + break; + case TAG_NAME: + ret = handle_name(data, context); + break; + case PARAM_TYPE: + ret = handle_param_type(data, context); + break; + case PARAM: + ret = handle_param(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + ret = false; + } + return ret; +} + +bool verify_field_struct(const s_field_ctx *context) { + uint8_t required_bits = 0; + + // check if struct version was provided + required_bits |= SET_BIT(BIT_VERSION); + if ((context->set_flags & required_bits) != required_bits) { + PRINTF("Error: no struct version specified!\n"); + return false; + } + + // verify required fields + switch (context->field->version) { + case 1: + required_bits |= SET_BIT(BIT_NAME) | SET_BIT(BIT_PARAM_TYPE) | SET_BIT(BIT_PARAM); + break; + default: + PRINTF("Error: unsupported field struct version (%u)\n", context->field->version); + return false; + } + + if ((context->set_flags & required_bits) != required_bits) { + PRINTF("Error: missing required field(s)\n"); + return false; + } + return true; +} + +bool format_field(const s_field *field) { + bool ret; + + switch (field->param_type) { + case PARAM_TYPE_RAW: + ret = format_param_raw(&field->param_raw, field->name); + break; + default: + ret = false; + } + return ret; +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/gtp_field.h b/src_features/generic_tx_parser/gtp_field.h new file mode 100644 index 000000000..893d36c16 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_field.h @@ -0,0 +1,31 @@ +#ifndef GTP_FIELD_H_ +#define GTP_FIELD_H_ + +#include +#include +#include "tlv.h" +#include "gtp_param_raw.h" + +typedef enum { + PARAM_TYPE_RAW = 0, +} e_param_type; + +typedef struct { + uint8_t version; + char name[21]; + e_param_type param_type; + union { + s_param_raw param_raw; + }; +} s_field; + +typedef struct { + s_field *field; + uint8_t set_flags; +} s_field_ctx; + +bool handle_field_struct(const s_tlv_data *data, s_field_ctx *context); +bool verify_field_struct(const s_field_ctx *context); +bool format_field(const s_field *field); + +#endif // !GTP_FIELD_H_ diff --git a/src_features/generic_tx_parser/gtp_field_table.c b/src_features/generic_tx_parser/gtp_field_table.c index f8cdf570b..39569710a 100644 --- a/src_features/generic_tx_parser/gtp_field_table.c +++ b/src_features/generic_tx_parser/gtp_field_table.c @@ -58,7 +58,7 @@ void field_table_cleanup(void) { } } -bool add_to_field_table(uint8_t type, const char *key, const char *value) { +bool add_to_field_table(e_param_type type, const char *key, const char *value) { int offset = 0; uint8_t *ptr; uint8_t key_len = strlen(key) + 1; diff --git a/src_features/generic_tx_parser/gtp_field_table.h b/src_features/generic_tx_parser/gtp_field_table.h index bfa1cde34..3b43890df 100644 --- a/src_features/generic_tx_parser/gtp_field_table.h +++ b/src_features/generic_tx_parser/gtp_field_table.h @@ -3,16 +3,17 @@ #include #include +#include "gtp_field.h" typedef struct { - uint8_t type; + e_param_type type; const char *key; const char *value; } s_field_table_entry; void field_table_init(void); void field_table_cleanup(void); -bool add_to_field_table(uint8_t type, const char *key, const char *value); +bool add_to_field_table(e_param_type type, const char *key, const char *value); size_t field_table_size(void); bool get_from_field_table(int index, s_field_table_entry *entry); diff --git a/src_features/generic_tx_parser/gtp_param_raw.c b/src_features/generic_tx_parser/gtp_param_raw.c new file mode 100644 index 000000000..3003835da --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_raw.c @@ -0,0 +1,188 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +#include +#include "os_print.h" +#include "gtp_param_raw.h" +#include "uint256.h" +#include "read.h" +#include "gtp_field_table.h" +#include "utils.h" +#include "shared_context.h" + +enum { + TAG_VERSION = 0x00, + TAG_VALUE = 0x01, +}; + +static bool handle_version(const s_tlv_data *data, s_param_raw_context *context) { + if (data->length != sizeof(context->param->version)) { + return false; + } + context->param->version = data->value[0]; + return true; +} + +static bool handle_value(const s_tlv_data *data, s_param_raw_context *context) { + s_value_context ctx = {0}; + + ctx.value = &context->param->value; + explicit_bzero(ctx.value, sizeof(*ctx.value)); + return tlv_parse(data->value, data->length, (f_tlv_data_handler) &handle_value_struct, &ctx); +} + +bool handle_param_raw_struct(const s_tlv_data *data, s_param_raw_context *context) { + bool ret; + + switch (data->tag) { + case TAG_VERSION: + ret = handle_version(data, context); + break; + case TAG_VALUE: + ret = handle_value(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + ret = false; + } + return ret; +} + +bool format_uint(const s_value *def, const s_parsed_value *value, char *buf, size_t buf_size) { + uint256_t value256; + + (void) def; + convertUint256BE(value->ptr, value->length, &value256); + return tostring256(&value256, 10, buf, buf_size); +} + +bool format_int(const s_value *def, const s_parsed_value *value, char *buf, size_t buf_size) { + uint8_t tmp[32]; + bool ret; + union { + uint256_t value256; + uint128_t value128; + int64_t value64; + int32_t value32; + int16_t value16; + int8_t value8; + } uv; + + buf_shrink_expand(value->ptr, value->length, tmp, def->type_size); + switch (def->type_size * 8) { + case 256: + convertUint256BE(tmp, def->type_size, &uv.value256); + ret = tostring256_signed(&uv.value256, 10, buf, buf_size); + break; + case 128: + convertUint128BE(tmp, def->type_size, &uv.value128); + ret = tostring128_signed(&uv.value128, 10, buf, buf_size); + break; + case 64: + uv.value64 = read_u64_be(tmp, 0); + ret = snprintf(buf, buf_size, "%" PRId64, uv.value64) > 0; + break; + case 32: + uv.value32 = read_u32_be(tmp, 0); + ret = snprintf(buf, buf_size, "%" PRId32, uv.value32) > 0; + break; + case 16: + uv.value16 = read_u16_be(tmp, 0); + ret = snprintf(buf, buf_size, "%u" PRId16, uv.value16) > 0; + break; + case 8: + uv.value8 = value->ptr[0]; + ret = snprintf(buf, buf_size, "%u" PRId8, uv.value8) > 0; + break; + default: + ret = false; + } + return ret; +} + +static bool format_addr(const s_value *def, + const s_parsed_value *value, + char *buf, + size_t buf_size) { + uint8_t tmp[ADDRESS_LENGTH]; + + (void) def; + buf_shrink_expand(value->ptr, value->length, tmp, sizeof(tmp)); + return getEthDisplayableAddress(tmp, buf, buf_size, chainConfig->chainId); +} + +static bool format_bool(const s_value *def, + const s_parsed_value *value, + char *buf, + size_t buf_size) { + uint8_t tmp; + + (void) def; + buf_shrink_expand(value->ptr, value->length, &tmp, 1); + snprintf(buf, buf_size, "%s", tmp ? "true" : "false"); + return true; +} + +static bool format_bytes(const s_value *def, + const s_parsed_value *value, + char *buf, + size_t buf_size) { + (void) def; +#pragma GCC diagnostic ignored "-Wformat" + snprintf(buf, buf_size, "0x%.*h", value->length, value->ptr); +#pragma GCC diagnostic warning "-Wformat" + if ((2 + (value->length * 2) + 1) > buf_size) { + memmove(&buf[buf_size - 1 - 3], "...", 3); + } + return true; +} + +static bool format_string(const s_value *def, + const s_parsed_value *value, + char *buf, + size_t buf_size) { + (void) def; + str_cpy_explicit_trunc((char *) value->ptr, value->length, buf, buf_size); + return true; +} + +bool format_param_raw(const s_param_raw *param, const char *name) { + bool ret = false; + s_parsed_value_collection collec; + char *buf = strings.tmp.tmp; + size_t buf_size = sizeof(strings.tmp.tmp); + + if (!value_get(¶m->value, &collec)) { + return false; + } + for (int i = 0; i < collec.size; ++i) { + switch (param->value.type_family) { + case TF_UINT: + ret = format_uint(¶m->value, &collec.value[i], buf, buf_size); + break; + case TF_INT: + ret = format_int(¶m->value, &collec.value[i], buf, buf_size); + break; + case TF_ADDRESS: + ret = format_addr(¶m->value, &collec.value[i], buf, buf_size); + break; + case TF_BOOL: + ret = format_bool(¶m->value, &collec.value[i], buf, buf_size); + break; + case TF_BYTES: + ret = format_bytes(¶m->value, &collec.value[i], buf, buf_size); + break; + case TF_STRING: + ret = format_string(¶m->value, &collec.value[i], buf, buf_size); + break; + case TF_UFIXED: + case TF_FIXED: + default: + ret = false; + } + if (ret) ret = add_to_field_table(PARAM_TYPE_RAW, name, buf); + if (!ret) break; + } + return ret; +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/gtp_param_raw.h b/src_features/generic_tx_parser/gtp_param_raw.h new file mode 100644 index 000000000..9f73c5ade --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_raw.h @@ -0,0 +1,21 @@ +#ifndef GTP_PARAM_RAW_H_ +#define GTP_PARAM_RAW_H_ + +#include +#include +#include "tlv.h" +#include "gtp_value.h" + +typedef struct { + uint8_t version; + s_value value; +} s_param_raw; + +typedef struct { + s_param_raw *param; +} s_param_raw_context; + +bool handle_param_raw_struct(const s_tlv_data *data, s_param_raw_context *context); +bool format_param_raw(const s_param_raw *param, const char *name); + +#endif // !GTP_PARAM_RAW_H_ diff --git a/src_features/generic_tx_parser/gtp_parsed_value.h b/src_features/generic_tx_parser/gtp_parsed_value.h new file mode 100644 index 000000000..2f7b4bcfc --- /dev/null +++ b/src_features/generic_tx_parser/gtp_parsed_value.h @@ -0,0 +1,18 @@ +#ifndef GTP_PARSED_VALUE_H_ +#define GTP_PARSED_VALUE_H_ + +#include + +typedef struct { + uint16_t length; + const uint8_t *ptr; +} s_parsed_value; + +#define MAX_VALUE_COLLECTION_SIZE 16 + +typedef struct { + uint8_t size; + s_parsed_value value[MAX_VALUE_COLLECTION_SIZE]; +} s_parsed_value_collection; + +#endif // !GTP_PARSED_VALUE_H_ diff --git a/src_features/generic_tx_parser/gtp_path_array.c b/src_features/generic_tx_parser/gtp_path_array.c new file mode 100644 index 000000000..84ef3af5c --- /dev/null +++ b/src_features/generic_tx_parser/gtp_path_array.c @@ -0,0 +1,62 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +#include +#include +#include "gtp_path_array.h" +#include "read.h" +#include "os_print.h" +#include "utils.h" + +enum { + TAG_WEIGHT = 0x01, + TAG_START = 0x02, + TAG_END = 0x03, +}; + +static bool handle_weight(const s_tlv_data *data, s_path_array_context *context) { + if (data->length != sizeof(context->args->weight)) { + return false; + } + context->args->weight = data->value[0]; + return true; +} + +static bool handle_start(const s_tlv_data *data, s_path_array_context *context) { + if (data->length != sizeof(context->args->start)) { + return false; + } + context->args->start = read_u16_be(data->value, 0); + context->args->has_start = true; + return true; +} + +static bool handle_end(const s_tlv_data *data, s_path_array_context *context) { + if (data->length != sizeof(context->args->end)) { + return false; + } + context->args->end = read_u16_be(data->value, 0); + context->args->has_end = true; + return true; +} + +bool handle_array_struct(const s_tlv_data *data, s_path_array_context *context) { + bool ret; + + switch (data->tag) { + case TAG_WEIGHT: + ret = handle_weight(data, context); + break; + case TAG_START: + ret = handle_start(data, context); + break; + case TAG_END: + ret = handle_end(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + ret = false; + } + return ret; +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/gtp_path_array.h b/src_features/generic_tx_parser/gtp_path_array.h new file mode 100644 index 000000000..acbfe2af2 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_path_array.h @@ -0,0 +1,22 @@ +#ifndef GTP_PATH_ARRAY_H_ +#define GTP_PATH_ARRAY_H_ + +#include +#include +#include "tlv.h" + +typedef struct { + uint8_t weight; + bool has_start; + int16_t start; + bool has_end; + int16_t end; +} s_array_args; + +typedef struct { + s_array_args *args; +} s_path_array_context; + +bool handle_array_struct(const s_tlv_data *data, s_path_array_context *context); + +#endif // !GTP_PATH_ARRAY_H_ diff --git a/src_features/generic_tx_parser/gtp_path_slice.c b/src_features/generic_tx_parser/gtp_path_slice.c new file mode 100644 index 000000000..ee5267ae0 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_path_slice.c @@ -0,0 +1,47 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +#include "gtp_path_slice.h" +#include "os_print.h" +#include "read.h" + +enum { + TAG_START = 0x01, + TAG_END = 0x02, +}; + +static bool handle_start(const s_tlv_data *data, s_path_slice_context *context) { + if (data->length != sizeof(context->args->start)) { + return false; + } + context->args->start = read_u16_be(data->value, 0); + context->args->has_start = true; + return true; +} + +static bool handle_end(const s_tlv_data *data, s_path_slice_context *context) { + if (data->length != sizeof(context->args->end)) { + return false; + } + context->args->end = read_u16_be(data->value, 0); + context->args->has_end = true; + return true; +} + +bool handle_slice_struct(const s_tlv_data *data, s_path_slice_context *context) { + bool ret; + + switch (data->tag) { + case TAG_START: + ret = handle_start(data, context); + break; + case TAG_END: + ret = handle_end(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + ret = false; + } + return ret; +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/gtp_path_slice.h b/src_features/generic_tx_parser/gtp_path_slice.h new file mode 100644 index 000000000..313c6aade --- /dev/null +++ b/src_features/generic_tx_parser/gtp_path_slice.h @@ -0,0 +1,21 @@ +#ifndef GTP_PATH_SLICE_H_ +#define GTP_PATH_SLICE_H_ + +#include +#include +#include "tlv.h" + +typedef struct { + bool has_start; + int16_t start; + bool has_end; + int16_t end; +} s_slice_args; + +typedef struct { + s_slice_args *args; +} s_path_slice_context; + +bool handle_slice_struct(const s_tlv_data *data, s_path_slice_context *context); + +#endif // !GTP_PATH_SLICE_H_ diff --git a/src_features/generic_tx_parser/gtp_value.c b/src_features/generic_tx_parser/gtp_value.c new file mode 100644 index 000000000..bc4c775cc --- /dev/null +++ b/src_features/generic_tx_parser/gtp_value.c @@ -0,0 +1,162 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +#include "os_print.h" +#include "gtp_value.h" +#include "gtp_data_path.h" +#include "shared_context.h" // txContext +#include "apdu_constants.h" // APDU_RESPONSE_OK +#include "feature_signTx.h" // get_public_key +#include "gtp_parsed_value.h" + +enum { + TAG_VERSION = 0x00, + TAG_TYPE_FAMILY = 0x01, + TAG_TYPE_SIZE = 0x02, + TAG_DATA_PATH = 0x03, + TAG_CONTAINER_PATH = 0x04, + TAG_CONSTANT = 0x05, +}; + +static bool handle_version(const s_tlv_data *data, s_value_context *context) { + if (data->length != sizeof(uint8_t)) { + return false; + } + context->value->version = data->value[0]; + return true; +} + +static bool handle_type_family(const s_tlv_data *data, s_value_context *context) { + if (data->length != sizeof(e_type_family)) { + return false; + } + context->value->type_family = data->value[0]; + return true; +} + +static bool handle_type_size(const s_tlv_data *data, s_value_context *context) { + if (data->length != sizeof(uint8_t)) { + return false; + } + context->value->type_size = data->value[0]; + return (context->value->type_size > 0) && (context->value->type_size <= 32); +} + +static bool handle_data_path(const s_tlv_data *data, s_value_context *context) { + s_data_path_context ctx = {0}; + + ctx.data_path = &context->value->data_path; + explicit_bzero(ctx.data_path, sizeof(*ctx.data_path)); + if (!tlv_parse(data->value, + data->length, + (f_tlv_data_handler) &handle_data_path_struct, + &ctx)) { + return false; + } + context->value->source = SOURCE_CALLDATA; + return true; +} + +static bool handle_container_path(const s_tlv_data *data, s_value_context *context) { + if (data->length != sizeof(e_container_path)) { + return false; + } + context->value->container_path = data->value[0]; + context->value->source = SOURCE_RLP; + return true; +} + +static bool handle_constant(const s_tlv_data *data, s_value_context *context) { + if (data->length > sizeof(context->value->constant.buf)) { + return false; + } + context->value->constant.size = data->length; + memcpy(context->value->constant.buf, data->value, data->length); + context->value->source = SOURCE_CONSTANT; + return true; +} + +bool handle_value_struct(const s_tlv_data *data, s_value_context *context) { + bool ret; + + switch (data->tag) { + case TAG_VERSION: + ret = handle_version(data, context); + break; + case TAG_TYPE_FAMILY: + ret = handle_type_family(data, context); + break; + case TAG_TYPE_SIZE: + ret = handle_type_size(data, context); + break; + case TAG_DATA_PATH: + ret = handle_data_path(data, context); + break; + case TAG_CONTAINER_PATH: + ret = handle_container_path(data, context); + break; + case TAG_CONSTANT: + ret = handle_constant(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + ret = false; + } + return ret; +} + +// have to be declared here since it is not stored anywhere else +static uint8_t from_address[ADDRESS_LENGTH]; + +bool value_get(const s_value *value, s_parsed_value_collection *collection) { + bool ret; + + switch (value->source) { + case SOURCE_CALLDATA: + collection->size = 0; + ret = data_path_get(&value->data_path, collection); + break; + + case SOURCE_RLP: + switch (value->container_path) { + case CP_FROM: + if ((ret = get_public_key(from_address, sizeof(from_address)) == + APDU_RESPONSE_OK)) { + collection->value[0].ptr = from_address; + collection->value[0].length = sizeof(from_address); + collection->size = 1; + } + break; + + case CP_TO: + collection->value[0].ptr = tmpContent.txContent.destination; + collection->value[0].length = tmpContent.txContent.destinationLength; + collection->size = 1; + ret = true; + break; + + case CP_VALUE: + collection->value[0].ptr = tmpContent.txContent.value.value; + collection->value[0].length = tmpContent.txContent.value.length; + collection->size = 1; + ret = true; + break; + + default: + ret = false; + } + break; + + case SOURCE_CONSTANT: + collection->value[0].ptr = value->constant.buf; + collection->value[0].length = value->constant.size; + collection->size = 1; + ret = true; + break; + + default: + ret = false; + } + return ret; +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/gtp_value.h b/src_features/generic_tx_parser/gtp_value.h new file mode 100644 index 000000000..c6a49b376 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_value.h @@ -0,0 +1,57 @@ +#ifndef GTP_VALUE_H_ +#define GTP_VALUE_H_ + +#include +#include +#include "tlv.h" +#include "gtp_data_path.h" +#include "calldata.h" + +typedef enum { + TF_UINT = 1, + TF_INT, + TF_UFIXED, + TF_FIXED, + TF_ADDRESS, + TF_BOOL, + TF_BYTES, + TF_STRING, +} e_type_family; + +typedef enum { + CP_FROM = 0, + CP_TO, + CP_VALUE, +} e_container_path; + +typedef enum { + SOURCE_CALLDATA, + SOURCE_RLP, + SOURCE_CONSTANT, +} e_value_source; + +typedef struct { + uint8_t size; + uint8_t buf[CALLDATA_CHUNK_SIZE]; +} s_constant; + +typedef struct { + uint8_t version; + e_type_family type_family; + uint8_t type_size; + union { + s_data_path data_path; + e_container_path container_path; + s_constant constant; + }; + e_value_source source; +} s_value; + +typedef struct { + s_value *value; +} s_value_context; + +bool handle_value_struct(const s_tlv_data *data, s_value_context *context); +bool value_get(const s_value *value, s_parsed_value_collection *collection); + +#endif // !GTP_VALUE_H_ From e2b87cf0e01b6012bed59420d5a305949b2ccf25 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 16 Dec 2024 16:03:46 +0100 Subject: [PATCH 33/45] Added GTP param_amount --- src_features/generic_tx_parser/gtp_field.c | 9 +++ src_features/generic_tx_parser/gtp_field.h | 3 + .../generic_tx_parser/gtp_param_amount.c | 75 +++++++++++++++++++ .../generic_tx_parser/gtp_param_amount.h | 21 ++++++ 4 files changed, 108 insertions(+) create mode 100644 src_features/generic_tx_parser/gtp_param_amount.c create mode 100644 src_features/generic_tx_parser/gtp_param_amount.h diff --git a/src_features/generic_tx_parser/gtp_field.c b/src_features/generic_tx_parser/gtp_field.c index 3562d7c0d..ff64fcf6e 100644 --- a/src_features/generic_tx_parser/gtp_field.c +++ b/src_features/generic_tx_parser/gtp_field.c @@ -21,6 +21,7 @@ enum { typedef union { s_param_raw_context raw_ctx; + s_param_amount_context amount_ctx; } u_param_context; static bool handle_version(const s_tlv_data *data, s_field_ctx *context) { @@ -52,6 +53,7 @@ static bool handle_param_type(const s_tlv_data *data, s_field_ctx *context) { context->field->param_type = data->value[0]; switch (context->field->param_type) { case PARAM_TYPE_RAW: + case PARAM_TYPE_AMOUNT: break; default: PRINTF("Error: Unsupported param type (%u)\n", context->field->param_type); @@ -78,6 +80,10 @@ static bool handle_param(const s_tlv_data *data, s_field_ctx *context) { handler = (f_tlv_data_handler) &handle_param_raw_struct; param_ctx.raw_ctx.param = &context->field->param_raw; break; + case PARAM_TYPE_AMOUNT: + handler = (f_tlv_data_handler) &handle_param_amount_struct; + param_ctx.amount_ctx.param = &context->field->param_amount; + break; default: return false; } @@ -142,6 +148,9 @@ bool format_field(const s_field *field) { case PARAM_TYPE_RAW: ret = format_param_raw(&field->param_raw, field->name); break; + case PARAM_TYPE_AMOUNT: + ret = format_param_amount(&field->param_amount, field->name); + break; default: ret = false; } diff --git a/src_features/generic_tx_parser/gtp_field.h b/src_features/generic_tx_parser/gtp_field.h index 893d36c16..2991eeb29 100644 --- a/src_features/generic_tx_parser/gtp_field.h +++ b/src_features/generic_tx_parser/gtp_field.h @@ -5,9 +5,11 @@ #include #include "tlv.h" #include "gtp_param_raw.h" +#include "gtp_param_amount.h" typedef enum { PARAM_TYPE_RAW = 0, + PARAM_TYPE_AMOUNT, } e_param_type; typedef struct { @@ -16,6 +18,7 @@ typedef struct { e_param_type param_type; union { s_param_raw param_raw; + s_param_amount param_amount; }; } s_field; diff --git a/src_features/generic_tx_parser/gtp_param_amount.c b/src_features/generic_tx_parser/gtp_param_amount.c new file mode 100644 index 000000000..a7de6918f --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_amount.c @@ -0,0 +1,75 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +#include "gtp_param_amount.h" +#include "network.h" +#include "common_utils.h" +#include "gtp_field_table.h" +#include "shared_context.h" + +enum { + TAG_VERSION = 0x00, + TAG_VALUE = 0x01, +}; + +static bool handle_version(const s_tlv_data *data, s_param_amount_context *context) { + if (data->length != sizeof(context->param->version)) { + return false; + } + context->param->version = data->value[0]; + return true; +} + +static bool handle_value(const s_tlv_data *data, s_param_amount_context *context) { + s_value_context ctx = {0}; + + ctx.value = &context->param->value; + explicit_bzero(ctx.value, sizeof(*ctx.value)); + return tlv_parse(data->value, data->length, (f_tlv_data_handler) &handle_value_struct, &ctx); +} + +bool handle_param_amount_struct(const s_tlv_data *data, s_param_amount_context *context) { + bool ret; + + switch (data->tag) { + case TAG_VERSION: + ret = handle_version(data, context); + break; + case TAG_VALUE: + ret = handle_value(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + ret = false; + } + return ret; +} + +bool format_param_amount(const s_param_amount *param, const char *name) { + uint64_t chain_id; + const char *ticker; + s_parsed_value_collection collec; + char *buf = strings.tmp.tmp; + size_t buf_size = sizeof(strings.tmp.tmp); + + if (!value_get(¶m->value, &collec)) { + return false; + } + chain_id = get_tx_chain_id(); + ticker = get_displayable_ticker(&chain_id, chainConfig); + for (int i = 0; i < collec.size; ++i) { + if (!amountToString(collec.value[i].ptr, + collec.value[i].length, + WEI_TO_ETHER, + ticker, + buf, + buf_size)) { + return false; + } + if (!add_to_field_table(PARAM_TYPE_AMOUNT, name, buf)) { + return false; + } + } + return true; +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/gtp_param_amount.h b/src_features/generic_tx_parser/gtp_param_amount.h new file mode 100644 index 000000000..e2c9eb81a --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_amount.h @@ -0,0 +1,21 @@ +#ifndef GTP_PARAM_AMOUNT_H_ +#define GTP_PARAM_AMOUNT_H_ + +#include +#include +#include "tlv.h" +#include "gtp_value.h" + +typedef struct { + uint8_t version; + s_value value; +} s_param_amount; + +typedef struct { + s_param_amount *param; +} s_param_amount_context; + +bool handle_param_amount_struct(const s_tlv_data *data, s_param_amount_context *context); +bool format_param_amount(const s_param_amount *param, const char *name); + +#endif // !GTP_PARAM_AMOUNT_H_ From 7513e9847dc17e31560d89d81cc9fb580f6e5530 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 16 Dec 2024 16:05:46 +0100 Subject: [PATCH 34/45] Added GTP param_token_amount --- src_features/generic_tx_parser/gtp_field.c | 9 + src_features/generic_tx_parser/gtp_field.h | 3 + .../gtp_param_token_amount.c | 185 ++++++++++++++++++ .../gtp_param_token_amount.h | 32 +++ 4 files changed, 229 insertions(+) create mode 100644 src_features/generic_tx_parser/gtp_param_token_amount.c create mode 100644 src_features/generic_tx_parser/gtp_param_token_amount.h diff --git a/src_features/generic_tx_parser/gtp_field.c b/src_features/generic_tx_parser/gtp_field.c index ff64fcf6e..c879db03e 100644 --- a/src_features/generic_tx_parser/gtp_field.c +++ b/src_features/generic_tx_parser/gtp_field.c @@ -22,6 +22,7 @@ enum { typedef union { s_param_raw_context raw_ctx; s_param_amount_context amount_ctx; + s_param_token_amount_context token_amount_ctx; } u_param_context; static bool handle_version(const s_tlv_data *data, s_field_ctx *context) { @@ -54,6 +55,7 @@ static bool handle_param_type(const s_tlv_data *data, s_field_ctx *context) { switch (context->field->param_type) { case PARAM_TYPE_RAW: case PARAM_TYPE_AMOUNT: + case PARAM_TYPE_TOKEN_AMOUNT: break; default: PRINTF("Error: Unsupported param type (%u)\n", context->field->param_type); @@ -84,6 +86,10 @@ static bool handle_param(const s_tlv_data *data, s_field_ctx *context) { handler = (f_tlv_data_handler) &handle_param_amount_struct; param_ctx.amount_ctx.param = &context->field->param_amount; break; + case PARAM_TYPE_TOKEN_AMOUNT: + handler = (f_tlv_data_handler) &handle_param_token_amount_struct; + param_ctx.token_amount_ctx.param = &context->field->param_token_amount; + break; default: return false; } @@ -151,6 +157,9 @@ bool format_field(const s_field *field) { case PARAM_TYPE_AMOUNT: ret = format_param_amount(&field->param_amount, field->name); break; + case PARAM_TYPE_TOKEN_AMOUNT: + ret = format_param_token_amount(&field->param_token_amount, field->name); + break; default: ret = false; } diff --git a/src_features/generic_tx_parser/gtp_field.h b/src_features/generic_tx_parser/gtp_field.h index 2991eeb29..cd92e352c 100644 --- a/src_features/generic_tx_parser/gtp_field.h +++ b/src_features/generic_tx_parser/gtp_field.h @@ -6,10 +6,12 @@ #include "tlv.h" #include "gtp_param_raw.h" #include "gtp_param_amount.h" +#include "gtp_param_token_amount.h" typedef enum { PARAM_TYPE_RAW = 0, PARAM_TYPE_AMOUNT, + PARAM_TYPE_TOKEN_AMOUNT, } e_param_type; typedef struct { @@ -19,6 +21,7 @@ typedef struct { union { s_param_raw param_raw; s_param_amount param_amount; + s_param_token_amount param_token_amount; }; } s_field; diff --git a/src_features/generic_tx_parser/gtp_param_token_amount.c b/src_features/generic_tx_parser/gtp_param_token_amount.c new file mode 100644 index 000000000..b68df1b31 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_token_amount.c @@ -0,0 +1,185 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +#include +#include "gtp_param_token_amount.h" +#include "network.h" +#include "utils.h" +#include "gtp_field_table.h" +#include "manage_asset_info.h" + +enum { + TAG_VERSION = 0x00, + TAG_VALUE = 0x01, + TAG_TOKEN = 0x02, + TAG_NATIVE_CURRENCY = 0x03, + TAG_THRESHOLD = 0x04, + TAG_ABOVE_THRESHOLD_MSG = 0x05, +}; + +static bool handle_version(const s_tlv_data *data, s_param_token_amount_context *context) { + if (data->length != sizeof(context->param->version)) { + return false; + } + context->param->version = data->value[0]; + return true; +} + +static bool handle_value(const s_tlv_data *data, s_param_token_amount_context *context) { + s_value_context ctx = {0}; + + ctx.value = &context->param->value; + explicit_bzero(ctx.value, sizeof(*ctx.value)); + return tlv_parse(data->value, data->length, (f_tlv_data_handler) &handle_value_struct, &ctx); +} + +static bool handle_token(const s_tlv_data *data, s_param_token_amount_context *context) { + s_value_context ctx = {0}; + + ctx.value = &context->param->token; + explicit_bzero(ctx.value, sizeof(*ctx.value)); + context->param->has_token = true; + return tlv_parse(data->value, data->length, (f_tlv_data_handler) &handle_value_struct, &ctx); +} + +static bool handle_native_currency(const s_tlv_data *data, s_param_token_amount_context *context) { + if (data->length > ADDRESS_LENGTH) { + return false; + } + if (context->param->native_addr_count == MAX_NATIVE_ADDRS) { + return false; + } + memcpy(&context->param + ->native_addrs[context->param->native_addr_count][ADDRESS_LENGTH - data->length], + data->value, + data->length); + context->param->native_addr_count += 1; + return true; +} + +static bool handle_threshold(const s_tlv_data *data, s_param_token_amount_context *context) { + if (data->length > sizeof(uint256_t)) { + return false; + } + convertUint256BE(data->value, data->length, &context->param->threshold); + return true; +} + +static bool handle_above_threshold_msg(const s_tlv_data *data, + s_param_token_amount_context *context) { + if (data->length >= sizeof(context->param->above_threshold_msg)) { + return false; + } + memcpy(context->param->above_threshold_msg, data->value, data->length); + context->param->above_threshold_msg[data->length] = '\0'; + return true; +} + +bool handle_param_token_amount_struct(const s_tlv_data *data, + s_param_token_amount_context *context) { + bool ret; + + switch (data->tag) { + case TAG_VERSION: + ret = handle_version(data, context); + break; + case TAG_VALUE: + ret = handle_value(data, context); + break; + case TAG_TOKEN: + ret = handle_token(data, context); + break; + case TAG_NATIVE_CURRENCY: + ret = handle_native_currency(data, context); + break; + case TAG_THRESHOLD: + ret = handle_threshold(data, context); + break; + case TAG_ABOVE_THRESHOLD_MSG: + ret = handle_above_threshold_msg(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + ret = false; + } + return ret; +} + +static bool match_native(const uint8_t *addr, const s_param_token_amount *param) { + for (int i = 0; i < param->native_addr_count; ++i) { + if (memcmp(addr, param->native_addrs[i], ADDRESS_LENGTH) == 0) { + return true; + } + } + return false; +} + +bool format_param_token_amount(const s_param_token_amount *param, const char *name) { + s_parsed_value_collection collec_value; + s_parsed_value_collection collec_token; + char *buf = strings.tmp.tmp; + size_t buf_size = sizeof(strings.tmp.tmp); + uint8_t addr_buf[ADDRESS_LENGTH]; + + if (!value_get(¶m->value, &collec_value)) { + return false; + } + if (param->has_token) { + if (!value_get(¶m->token, &collec_token)) { + return false; + } + if (collec_value.size != collec_token.size) { + PRINTF("Error: mismatch between counts of value & token!\n"); + return false; + } + } else { + explicit_bzero(&collec_token, sizeof(collec_token)); + } + uint64_t chain_id = get_tx_chain_id(); + const char *ticker = g_unknown_ticker; + uint8_t decimals = 0; + for (int i = 0; i < collec_value.size; ++i) { + if (param->has_token) { + buf_shrink_expand(collec_token.value[i].ptr, + collec_token.value[i].length, + addr_buf, + sizeof(addr_buf)); + if (match_native(addr_buf, param)) { + ticker = get_displayable_ticker(&chain_id, chainConfig); + decimals = WEI_TO_ETHER; + } else { + const tokenDefinition_t *token; + if ((token = (const tokenDefinition_t *) get_asset_info_by_addr(addr_buf)) != + NULL) { + ticker = token->ticker; + decimals = token->decimals; + } + } + } + uint256_t zero256 = {0}; + uint256_t val256; + + convertUint256BE(collec_value.value[i].ptr, collec_value.value[i].length, &val256); + if (!equal256(¶m->threshold, &zero256) && gte256(&val256, ¶m->threshold)) { + if (param->above_threshold_msg[0] != '\0') { + snprintf(buf, buf_size, "%s %s", param->above_threshold_msg, ticker); + } else { + snprintf(buf, buf_size, "Unlimited %s", ticker); + } + } else { + if (!amountToString(collec_value.value[i].ptr, + collec_value.value[i].length, + decimals, + ticker, + buf, + buf_size)) { + return false; + } + } + if (!add_to_field_table(PARAM_TYPE_AMOUNT, name, buf)) { + return false; + } + } + return true; +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/gtp_param_token_amount.h b/src_features/generic_tx_parser/gtp_param_token_amount.h new file mode 100644 index 000000000..fb29a8391 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_token_amount.h @@ -0,0 +1,32 @@ +#ifndef GTP_PARAM_TOKEN_AMOUNT_H_ +#define GTP_PARAM_TOKEN_AMOUNT_H_ + +#include +#include +#include "uint256.h" +#include "tlv.h" +#include "gtp_value.h" +#include "common_utils.h" + +#define MAX_NATIVE_ADDRS 4 + +typedef struct { + uint8_t version; + s_value value; + bool has_token; + s_value token; + uint8_t native_addr_count; + uint8_t native_addrs[MAX_NATIVE_ADDRS][ADDRESS_LENGTH]; + uint256_t threshold; + char above_threshold_msg[21]; +} s_param_token_amount; + +typedef struct { + s_param_token_amount *param; +} s_param_token_amount_context; + +bool handle_param_token_amount_struct(const s_tlv_data *data, + s_param_token_amount_context *context); +bool format_param_token_amount(const s_param_token_amount *param, const char *name); + +#endif // !GTP_PARAM_TOKEN_AMOUNT_H_ From 3dcf42a1d40d94efd735c7b04b6ebbd150bcd11a Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 16 Dec 2024 16:58:34 +0100 Subject: [PATCH 35/45] Added GTP param_nft --- src_features/generic_tx_parser/gtp_field.c | 17 ++++ src_features/generic_tx_parser/gtp_field.h | 5 + .../generic_tx_parser/gtp_param_nft.c | 98 +++++++++++++++++++ .../generic_tx_parser/gtp_param_nft.h | 26 +++++ 4 files changed, 146 insertions(+) create mode 100644 src_features/generic_tx_parser/gtp_param_nft.c create mode 100644 src_features/generic_tx_parser/gtp_param_nft.h diff --git a/src_features/generic_tx_parser/gtp_field.c b/src_features/generic_tx_parser/gtp_field.c index c879db03e..44705d4ac 100644 --- a/src_features/generic_tx_parser/gtp_field.c +++ b/src_features/generic_tx_parser/gtp_field.c @@ -23,6 +23,9 @@ typedef union { s_param_raw_context raw_ctx; s_param_amount_context amount_ctx; s_param_token_amount_context token_amount_ctx; +#ifdef HAVE_NFT_SUPPORT + s_param_nft_context nft_ctx; +#endif } u_param_context; static bool handle_version(const s_tlv_data *data, s_field_ctx *context) { @@ -56,6 +59,9 @@ static bool handle_param_type(const s_tlv_data *data, s_field_ctx *context) { case PARAM_TYPE_RAW: case PARAM_TYPE_AMOUNT: case PARAM_TYPE_TOKEN_AMOUNT: +#ifdef HAVE_NFT_SUPPORT + case PARAM_TYPE_NFT: +#endif break; default: PRINTF("Error: Unsupported param type (%u)\n", context->field->param_type); @@ -90,6 +96,12 @@ static bool handle_param(const s_tlv_data *data, s_field_ctx *context) { handler = (f_tlv_data_handler) &handle_param_token_amount_struct; param_ctx.token_amount_ctx.param = &context->field->param_token_amount; break; +#ifdef HAVE_NFT_SUPPORT + case PARAM_TYPE_NFT: + handler = (f_tlv_data_handler) &handle_param_nft_struct; + param_ctx.nft_ctx.param = &context->field->param_nft; + break; +#endif default: return false; } @@ -160,6 +172,11 @@ bool format_field(const s_field *field) { case PARAM_TYPE_TOKEN_AMOUNT: ret = format_param_token_amount(&field->param_token_amount, field->name); break; +#ifdef HAVE_NFT_SUPPORT + case PARAM_TYPE_NFT: + ret = format_param_nft(&field->param_nft, field->name); + break; +#endif default: ret = false; } diff --git a/src_features/generic_tx_parser/gtp_field.h b/src_features/generic_tx_parser/gtp_field.h index cd92e352c..1619cad27 100644 --- a/src_features/generic_tx_parser/gtp_field.h +++ b/src_features/generic_tx_parser/gtp_field.h @@ -7,11 +7,13 @@ #include "gtp_param_raw.h" #include "gtp_param_amount.h" #include "gtp_param_token_amount.h" +#include "gtp_param_nft.h" typedef enum { PARAM_TYPE_RAW = 0, PARAM_TYPE_AMOUNT, PARAM_TYPE_TOKEN_AMOUNT, + PARAM_TYPE_NFT, } e_param_type; typedef struct { @@ -22,6 +24,9 @@ typedef struct { s_param_raw param_raw; s_param_amount param_amount; s_param_token_amount param_token_amount; +#ifdef HAVE_NFT_SUPPORT + s_param_nft param_nft; +#endif }; } s_field; diff --git a/src_features/generic_tx_parser/gtp_param_nft.c b/src_features/generic_tx_parser/gtp_param_nft.c new file mode 100644 index 000000000..9a3d97b01 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_nft.c @@ -0,0 +1,98 @@ +#ifdef HAVE_GENERIC_TX_PARSER +#ifdef HAVE_NFT_SUPPORT + +#include "gtp_param_nft.h" +#include "manage_asset_info.h" +#include "utils.h" +#include "gtp_field_table.h" + +enum { TAG_VERSION = 0x00, TAG_ID = 0x01, TAG_COLLECTION = 0x02 }; + +static bool handle_version(const s_tlv_data *data, s_param_nft_context *context) { + if (data->length != sizeof(context->param->version)) { + return false; + } + context->param->version = data->value[0]; + return true; +} + +static bool handle_id(const s_tlv_data *data, s_param_nft_context *context) { + s_value_context ctx = {0}; + + ctx.value = &context->param->id; + explicit_bzero(ctx.value, sizeof(*ctx.value)); + return tlv_parse(data->value, data->length, (f_tlv_data_handler) &handle_value_struct, &ctx); +} + +static bool handle_collection(const s_tlv_data *data, s_param_nft_context *context) { + s_value_context ctx = {0}; + + ctx.value = &context->param->collection; + explicit_bzero(ctx.value, sizeof(*ctx.value)); + return tlv_parse(data->value, data->length, (f_tlv_data_handler) &handle_value_struct, &ctx); +} + +bool handle_param_nft_struct(const s_tlv_data *data, s_param_nft_context *context) { + bool ret; + + switch (data->tag) { + case TAG_VERSION: + ret = handle_version(data, context); + break; + case TAG_ID: + ret = handle_id(data, context); + break; + case TAG_COLLECTION: + ret = handle_collection(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + ret = false; + } + return ret; +} + +bool format_param_nft(const s_param_nft *param, const char *name) { + s_parsed_value_collection collections; + s_parsed_value_collection ids; + const extraInfo_t *asset; + char *buf = strings.tmp.tmp; + size_t buf_size = sizeof(strings.tmp.tmp); + uint8_t collection_idx; + uint8_t addr_buf[ADDRESS_LENGTH]; + char tmp[80]; + + if (!value_get(¶m->collection, &collections)) { + return false; + } + if (!value_get(¶m->id, &ids)) { + return false; + } + if (collections.size == 0) { + return false; + } + if ((collections.size != 1) && (collections.size != ids.size)) { + return false; + } + for (int i = 0; i < ids.size; ++i) { + collection_idx = (i >= collections.size) ? 0 : i; + buf_shrink_expand(collections.value[collection_idx].ptr, + collections.value[collection_idx].length, + addr_buf, + sizeof(addr_buf)); + if ((asset = (const extraInfo_t *) get_asset_info_by_addr(addr_buf)) == NULL) { + return false; + } + if (!uint256_to_decimal(ids.value[i].ptr, ids.value[i].length, tmp, sizeof(tmp))) { + return false; + } + snprintf(buf, buf_size, "%s #%s", asset->nft.collectionName, tmp); + if (!add_to_field_table(PARAM_TYPE_NFT, name, buf)) { + return false; + } + } + return true; +} + +#endif // HAVE_NFT_SUPPORT +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/gtp_param_nft.h b/src_features/generic_tx_parser/gtp_param_nft.h new file mode 100644 index 000000000..f7fcfc1ab --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_nft.h @@ -0,0 +1,26 @@ +#ifndef GTP_PARAM_NFT_H_ +#define GTP_PARAM_NFT_H_ + +#ifdef HAVE_NFT_SUPPORT + +#include +#include +#include "tlv.h" +#include "gtp_value.h" + +typedef struct { + uint8_t version; + s_value id; + s_value collection; +} s_param_nft; + +typedef struct { + s_param_nft *param; +} s_param_nft_context; + +bool handle_param_nft_struct(const s_tlv_data *data, s_param_nft_context *context); +bool format_param_nft(const s_param_nft *param, const char *name); + +#endif + +#endif // !GTP_PARAM_NFT_H_ From 4d137b917d736df4bcb52fa495fecb7d6c36e988 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 16 Dec 2024 17:08:35 +0100 Subject: [PATCH 36/45] Added GTP param_datetime --- src_features/generic_tx_parser/gtp_field.c | 9 ++ src_features/generic_tx_parser/gtp_field.h | 3 + .../generic_tx_parser/gtp_param_datetime.c | 104 ++++++++++++++++++ .../generic_tx_parser/gtp_param_datetime.h | 27 +++++ 4 files changed, 143 insertions(+) create mode 100644 src_features/generic_tx_parser/gtp_param_datetime.c create mode 100644 src_features/generic_tx_parser/gtp_param_datetime.h diff --git a/src_features/generic_tx_parser/gtp_field.c b/src_features/generic_tx_parser/gtp_field.c index 44705d4ac..07b3f4247 100644 --- a/src_features/generic_tx_parser/gtp_field.c +++ b/src_features/generic_tx_parser/gtp_field.c @@ -26,6 +26,7 @@ typedef union { #ifdef HAVE_NFT_SUPPORT s_param_nft_context nft_ctx; #endif + s_param_datetime_context datetime_ctx; } u_param_context; static bool handle_version(const s_tlv_data *data, s_field_ctx *context) { @@ -62,6 +63,7 @@ static bool handle_param_type(const s_tlv_data *data, s_field_ctx *context) { #ifdef HAVE_NFT_SUPPORT case PARAM_TYPE_NFT: #endif + case PARAM_TYPE_DATETIME: break; default: PRINTF("Error: Unsupported param type (%u)\n", context->field->param_type); @@ -102,6 +104,10 @@ static bool handle_param(const s_tlv_data *data, s_field_ctx *context) { param_ctx.nft_ctx.param = &context->field->param_nft; break; #endif + case PARAM_TYPE_DATETIME: + handler = (f_tlv_data_handler) &handle_param_datetime_struct; + param_ctx.datetime_ctx.param = &context->field->param_datetime; + break; default: return false; } @@ -177,6 +183,9 @@ bool format_field(const s_field *field) { ret = format_param_nft(&field->param_nft, field->name); break; #endif + case PARAM_TYPE_DATETIME: + ret = format_param_datetime(&field->param_datetime, field->name); + break; default: ret = false; } diff --git a/src_features/generic_tx_parser/gtp_field.h b/src_features/generic_tx_parser/gtp_field.h index 1619cad27..33a886b39 100644 --- a/src_features/generic_tx_parser/gtp_field.h +++ b/src_features/generic_tx_parser/gtp_field.h @@ -8,12 +8,14 @@ #include "gtp_param_amount.h" #include "gtp_param_token_amount.h" #include "gtp_param_nft.h" +#include "gtp_param_datetime.h" typedef enum { PARAM_TYPE_RAW = 0, PARAM_TYPE_AMOUNT, PARAM_TYPE_TOKEN_AMOUNT, PARAM_TYPE_NFT, + PARAM_TYPE_DATETIME, } e_param_type; typedef struct { @@ -27,6 +29,7 @@ typedef struct { #ifdef HAVE_NFT_SUPPORT s_param_nft param_nft; #endif + s_param_datetime param_datetime; }; } s_field; diff --git a/src_features/generic_tx_parser/gtp_param_datetime.c b/src_features/generic_tx_parser/gtp_param_datetime.c new file mode 100644 index 000000000..ca3fdade0 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_datetime.c @@ -0,0 +1,104 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +#include "gtp_param_datetime.h" +#include "gtp_field_table.h" +#include "read.h" +#include "time_format.h" +#include "utils.h" +#include "shared_context.h" + +enum { + TAG_VERSION = 0x00, + TAG_VALUE = 0x01, + TAG_TYPE = 0x02, +}; + +static bool handle_version(const s_tlv_data *data, s_param_datetime_context *context) { + if (data->length != sizeof(context->param->version)) { + return false; + } + context->param->version = data->value[0]; + return true; +} + +static bool handle_value(const s_tlv_data *data, s_param_datetime_context *context) { + s_value_context ctx = {0}; + + ctx.value = &context->param->value; + explicit_bzero(ctx.value, sizeof(*ctx.value)); + return tlv_parse(data->value, data->length, (f_tlv_data_handler) &handle_value_struct, &ctx); +} + +static bool handle_type(const s_tlv_data *data, s_param_datetime_context *context) { + if (data->length != sizeof(context->param->type)) { + return false; + } + switch (data->value[0]) { + case DT_UNIX: + case DT_BLOCKHEIGHT: + break; + default: + return false; + } + context->param->type = data->value[0]; + return true; +} + +bool handle_param_datetime_struct(const s_tlv_data *data, s_param_datetime_context *context) { + bool ret; + + switch (data->tag) { + case TAG_VERSION: + ret = handle_version(data, context); + break; + case TAG_VALUE: + ret = handle_value(data, context); + break; + case TAG_TYPE: + ret = handle_type(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + ret = false; + } + return ret; +} + +bool format_param_datetime(const s_param_datetime *param, const char *name) { + s_parsed_value_collection collec; + char *buf = strings.tmp.tmp; + size_t buf_size = sizeof(strings.tmp.tmp); + uint8_t time_buf[sizeof(uint32_t)]; + time_t timestamp; + uint256_t block_height; + + if (!value_get(¶m->value, &collec)) { + return false; + } + for (int i = 0; i < collec.size; ++i) { + switch (param->type) { + case DT_UNIX: + buf_shrink_expand(collec.value[i].ptr, + collec.value[i].length, + time_buf, + sizeof(time_buf)); + timestamp = read_u32_be(time_buf, 0); + if (!time_format_to_utc(×tamp, buf, buf_size)) { + return false; + } + break; + case DT_BLOCKHEIGHT: + convertUint256BE(collec.value[i].ptr, collec.value[i].length, &block_height); + if (!tostring256(&block_height, 10, buf, buf_size)) { + return false; + } + break; + } + if (!add_to_field_table(PARAM_TYPE_DATETIME, name, buf)) { + return false; + } + } + return true; +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/gtp_param_datetime.h b/src_features/generic_tx_parser/gtp_param_datetime.h new file mode 100644 index 000000000..0d3c955c2 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_datetime.h @@ -0,0 +1,27 @@ +#ifndef GTP_PARAM_DATETIME_H_ +#define GTP_PARAM_DATETIME_H_ + +#include +#include +#include "tlv.h" +#include "gtp_value.h" + +typedef enum { + DT_UNIX = 0, + DT_BLOCKHEIGHT = 1, +} e_datetime_type; + +typedef struct { + uint8_t version; + s_value value; + e_datetime_type type; +} s_param_datetime; + +typedef struct { + s_param_datetime *param; +} s_param_datetime_context; + +bool handle_param_datetime_struct(const s_tlv_data *data, s_param_datetime_context *context); +bool format_param_datetime(const s_param_datetime *param, const char *name); + +#endif // !GTP_PARAM_DATETIME_H_ From ccdf5d4a18d69a3c14e7abee95494584a06f3054 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 16 Dec 2024 17:02:21 +0100 Subject: [PATCH 37/45] Added GTP param_duration --- src_features/generic_tx_parser/gtp_field.c | 9 ++ src_features/generic_tx_parser/gtp_field.h | 3 + .../generic_tx_parser/gtp_param_duration.c | 107 ++++++++++++++++++ .../generic_tx_parser/gtp_param_duration.h | 21 ++++ 4 files changed, 140 insertions(+) create mode 100644 src_features/generic_tx_parser/gtp_param_duration.c create mode 100644 src_features/generic_tx_parser/gtp_param_duration.h diff --git a/src_features/generic_tx_parser/gtp_field.c b/src_features/generic_tx_parser/gtp_field.c index 07b3f4247..aeec3ddcc 100644 --- a/src_features/generic_tx_parser/gtp_field.c +++ b/src_features/generic_tx_parser/gtp_field.c @@ -27,6 +27,7 @@ typedef union { s_param_nft_context nft_ctx; #endif s_param_datetime_context datetime_ctx; + s_param_duration_context duration_ctx; } u_param_context; static bool handle_version(const s_tlv_data *data, s_field_ctx *context) { @@ -64,6 +65,7 @@ static bool handle_param_type(const s_tlv_data *data, s_field_ctx *context) { case PARAM_TYPE_NFT: #endif case PARAM_TYPE_DATETIME: + case PARAM_TYPE_DURATION: break; default: PRINTF("Error: Unsupported param type (%u)\n", context->field->param_type); @@ -108,6 +110,10 @@ static bool handle_param(const s_tlv_data *data, s_field_ctx *context) { handler = (f_tlv_data_handler) &handle_param_datetime_struct; param_ctx.datetime_ctx.param = &context->field->param_datetime; break; + case PARAM_TYPE_DURATION: + handler = (f_tlv_data_handler) &handle_param_duration_struct; + param_ctx.duration_ctx.param = &context->field->param_duration; + break; default: return false; } @@ -186,6 +192,9 @@ bool format_field(const s_field *field) { case PARAM_TYPE_DATETIME: ret = format_param_datetime(&field->param_datetime, field->name); break; + case PARAM_TYPE_DURATION: + ret = format_param_duration(&field->param_duration, field->name); + break; default: ret = false; } diff --git a/src_features/generic_tx_parser/gtp_field.h b/src_features/generic_tx_parser/gtp_field.h index 33a886b39..f10df171a 100644 --- a/src_features/generic_tx_parser/gtp_field.h +++ b/src_features/generic_tx_parser/gtp_field.h @@ -9,6 +9,7 @@ #include "gtp_param_token_amount.h" #include "gtp_param_nft.h" #include "gtp_param_datetime.h" +#include "gtp_param_duration.h" typedef enum { PARAM_TYPE_RAW = 0, @@ -16,6 +17,7 @@ typedef enum { PARAM_TYPE_TOKEN_AMOUNT, PARAM_TYPE_NFT, PARAM_TYPE_DATETIME, + PARAM_TYPE_DURATION, } e_param_type; typedef struct { @@ -30,6 +32,7 @@ typedef struct { s_param_nft param_nft; #endif s_param_datetime param_datetime; + s_param_duration param_duration; }; } s_field; diff --git a/src_features/generic_tx_parser/gtp_param_duration.c b/src_features/generic_tx_parser/gtp_param_duration.c new file mode 100644 index 000000000..d55a94e58 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_duration.c @@ -0,0 +1,107 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +#include "gtp_param_duration.h" +#include "read.h" +#include "gtp_field_table.h" +#include "utils.h" +#include "shared_context.h" + +#define SECONDS_IN_MINUTE 60 +#define MINUTES_IN_HOUR 60 +#define SECONDS_IN_HOUR (SECONDS_IN_MINUTE * MINUTES_IN_HOUR) +#define HOURS_IN_DAY 24 +#define MINUTES_IN_DAY (HOURS_IN_DAY * MINUTES_IN_HOUR) +#define SECONDS_IN_DAY (MINUTES_IN_DAY * SECONDS_IN_MINUTE) + +enum { + TAG_VERSION = 0x00, + TAG_VALUE = 0x01, +}; + +static bool handle_version(const s_tlv_data *data, s_param_duration_context *context) { + if (data->length != sizeof(context->param->version)) { + return false; + } + context->param->version = data->value[0]; + return true; +} + +static bool handle_value(const s_tlv_data *data, s_param_duration_context *context) { + s_value_context ctx = {0}; + + ctx.value = &context->param->value; + explicit_bzero(ctx.value, sizeof(*ctx.value)); + return tlv_parse(data->value, data->length, (f_tlv_data_handler) &handle_value_struct, &ctx); +} + +bool handle_param_duration_struct(const s_tlv_data *data, s_param_duration_context *context) { + bool ret; + + switch (data->tag) { + case TAG_VERSION: + ret = handle_version(data, context); + break; + case TAG_VALUE: + ret = handle_value(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + ret = false; + } + return ret; +} + +bool format_param_duration(const s_param_duration *param, const char *name) { + s_parsed_value_collection collec; + char *buf = strings.tmp.tmp; + size_t buf_size = sizeof(strings.tmp.tmp); + uint16_t days; + uint8_t hours; + uint8_t minutes; + uint8_t seconds; + uint64_t remaining; + uint8_t raw_buf[sizeof(remaining)]; + int off; + + if (!value_get(¶m->value, &collec)) { + return false; + } + for (int i = 0; i < collec.size; ++i) { + off = 0; + buf_shrink_expand(collec.value[i].ptr, collec.value[i].length, raw_buf, sizeof(raw_buf)); + remaining = read_u64_be(raw_buf, 0); + + days = remaining / SECONDS_IN_DAY; + if (days > 0) { + snprintf(&buf[off], buf_size - off, "%dd", days); + off = strlen(buf); + } + remaining %= SECONDS_IN_DAY; + + hours = remaining / SECONDS_IN_HOUR; + if (hours > 0) { + snprintf(&buf[off], buf_size - off, "%02dh", hours); + off = strlen(buf); + } + remaining %= SECONDS_IN_HOUR; + + minutes = remaining / SECONDS_IN_MINUTE; + if (minutes > 0) { + snprintf(&buf[off], buf_size - off, "%02dm", minutes); + off = strlen(buf); + } + remaining %= SECONDS_IN_MINUTE; + + seconds = (uint8_t) remaining; + if ((seconds > 0) || (off == 0)) { + snprintf(&buf[off], buf_size - off, "%02ds", seconds); + } + + if (!add_to_field_table(PARAM_TYPE_DURATION, name, buf)) { + return false; + } + } + return true; +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/gtp_param_duration.h b/src_features/generic_tx_parser/gtp_param_duration.h new file mode 100644 index 000000000..c1fb74632 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_duration.h @@ -0,0 +1,21 @@ +#ifndef GTP_PARAM_DURATION_H_ +#define GTP_PARAM_DURATION_H_ + +#include +#include +#include "tlv.h" +#include "gtp_value.h" + +typedef struct { + uint8_t version; + s_value value; +} s_param_duration; + +typedef struct { + s_param_duration *param; +} s_param_duration_context; + +bool handle_param_duration_struct(const s_tlv_data *data, s_param_duration_context *context); +bool format_param_duration(const s_param_duration *param, const char *name); + +#endif // !GTP_PARAM_DURATION_H_ From 3e375e30001ae420e1dd3859af99d02434371fad Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 16 Dec 2024 17:03:48 +0100 Subject: [PATCH 38/45] Added GTP param_unit --- src_features/generic_tx_parser/gtp_field.c | 9 ++ src_features/generic_tx_parser/gtp_field.h | 3 + .../generic_tx_parser/gtp_param_unit.c | 113 ++++++++++++++++++ .../generic_tx_parser/gtp_param_unit.h | 23 ++++ 4 files changed, 148 insertions(+) create mode 100644 src_features/generic_tx_parser/gtp_param_unit.c create mode 100644 src_features/generic_tx_parser/gtp_param_unit.h diff --git a/src_features/generic_tx_parser/gtp_field.c b/src_features/generic_tx_parser/gtp_field.c index aeec3ddcc..7dceef4d8 100644 --- a/src_features/generic_tx_parser/gtp_field.c +++ b/src_features/generic_tx_parser/gtp_field.c @@ -28,6 +28,7 @@ typedef union { #endif s_param_datetime_context datetime_ctx; s_param_duration_context duration_ctx; + s_param_unit_context unit_ctx; } u_param_context; static bool handle_version(const s_tlv_data *data, s_field_ctx *context) { @@ -66,6 +67,7 @@ static bool handle_param_type(const s_tlv_data *data, s_field_ctx *context) { #endif case PARAM_TYPE_DATETIME: case PARAM_TYPE_DURATION: + case PARAM_TYPE_UNIT: break; default: PRINTF("Error: Unsupported param type (%u)\n", context->field->param_type); @@ -114,6 +116,10 @@ static bool handle_param(const s_tlv_data *data, s_field_ctx *context) { handler = (f_tlv_data_handler) &handle_param_duration_struct; param_ctx.duration_ctx.param = &context->field->param_duration; break; + case PARAM_TYPE_UNIT: + handler = (f_tlv_data_handler) &handle_param_unit_struct; + param_ctx.unit_ctx.param = &context->field->param_unit; + break; default: return false; } @@ -195,6 +201,9 @@ bool format_field(const s_field *field) { case PARAM_TYPE_DURATION: ret = format_param_duration(&field->param_duration, field->name); break; + case PARAM_TYPE_UNIT: + ret = format_param_unit(&field->param_unit, field->name); + break; default: ret = false; } diff --git a/src_features/generic_tx_parser/gtp_field.h b/src_features/generic_tx_parser/gtp_field.h index f10df171a..db75e954b 100644 --- a/src_features/generic_tx_parser/gtp_field.h +++ b/src_features/generic_tx_parser/gtp_field.h @@ -10,6 +10,7 @@ #include "gtp_param_nft.h" #include "gtp_param_datetime.h" #include "gtp_param_duration.h" +#include "gtp_param_unit.h" typedef enum { PARAM_TYPE_RAW = 0, @@ -18,6 +19,7 @@ typedef enum { PARAM_TYPE_NFT, PARAM_TYPE_DATETIME, PARAM_TYPE_DURATION, + PARAM_TYPE_UNIT, } e_param_type; typedef struct { @@ -33,6 +35,7 @@ typedef struct { #endif s_param_datetime param_datetime; s_param_duration param_duration; + s_param_unit param_unit; }; } s_field; diff --git a/src_features/generic_tx_parser/gtp_param_unit.c b/src_features/generic_tx_parser/gtp_param_unit.c new file mode 100644 index 000000000..af00f84cb --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_unit.c @@ -0,0 +1,113 @@ +#ifdef HAVE_GENERIC_TX_PARSER + +#include "gtp_param_unit.h" +#include "gtp_field_table.h" +#include "shared_context.h" + +enum { + TAG_VERSION = 0x00, + TAG_VALUE = 0x01, + TAG_BASE = 0x02, + TAG_DECIMALS = 0x03, + TAG_PREFIX = 0x04, +}; + +static bool handle_version(const s_tlv_data *data, s_param_unit_context *context) { + if (data->length != sizeof(context->param->version)) { + return false; + } + context->param->version = data->value[0]; + return true; +} + +static bool handle_value(const s_tlv_data *data, s_param_unit_context *context) { + s_value_context ctx = {0}; + + ctx.value = &context->param->value; + explicit_bzero(ctx.value, sizeof(*ctx.value)); + return tlv_parse(data->value, data->length, (f_tlv_data_handler) &handle_value_struct, &ctx); +} + +static bool handle_base(const s_tlv_data *data, s_param_unit_context *context) { + if (data->length >= sizeof(context->param->base)) { + return false; + } + memcpy(context->param->base, data->value, data->length); + context->param->base[data->length] = '\0'; + return true; +} + +static bool handle_decimals(const s_tlv_data *data, s_param_unit_context *context) { + if (data->length != sizeof(context->param->decimals)) { + return false; + } + context->param->decimals = data->value[0]; + return true; +} + +static bool handle_prefix(const s_tlv_data *data, s_param_unit_context *context) { + if (data->length != sizeof(bool)) { + return false; + } + (void) context; + // unused for now + return true; +} + +bool handle_param_unit_struct(const s_tlv_data *data, s_param_unit_context *context) { + bool ret; + + switch (data->tag) { + case TAG_VERSION: + ret = handle_version(data, context); + break; + case TAG_VALUE: + ret = handle_value(data, context); + break; + case TAG_BASE: + ret = handle_base(data, context); + break; + case TAG_DECIMALS: + ret = handle_decimals(data, context); + break; + case TAG_PREFIX: + ret = handle_prefix(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + ret = false; + } + return ret; +} + +bool format_param_unit(const s_param_unit *param, const char *name) { + s_parsed_value_collection collec; + char *buf = strings.tmp.tmp; + size_t buf_size = sizeof(strings.tmp.tmp); + char tmp[80]; + size_t off; + + if (!value_get(¶m->value, &collec)) { + return false; + } + for (int i = 0; i < collec.size; ++i) { + if (!uint256_to_decimal(collec.value[i].ptr, collec.value[i].length, tmp, sizeof(tmp))) { + return false; + } + if (!adjustDecimals(tmp, strnlen(tmp, sizeof(tmp)), buf, buf_size, param->decimals)) { + return false; + } + if (param->base[0] == '\0') { + return false; + } + off = strlen(buf); + snprintf(&buf[off], buf_size - off, " %s", param->base); + + if (!add_to_field_table(PARAM_TYPE_UNIT, name, buf)) { + return false; + } + } + return true; +} + +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/gtp_param_unit.h b/src_features/generic_tx_parser/gtp_param_unit.h new file mode 100644 index 000000000..9930ce312 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_unit.h @@ -0,0 +1,23 @@ +#ifndef GTP_PARAM_UNIT_H_ +#define GTP_PARAM_UNIT_H_ + +#include +#include +#include "tlv.h" +#include "gtp_value.h" + +typedef struct { + uint8_t version; + s_value value; + char base[11]; + uint8_t decimals; +} s_param_unit; + +typedef struct { + s_param_unit *param; +} s_param_unit_context; + +bool handle_param_unit_struct(const s_tlv_data *data, s_param_unit_context *context); +bool format_param_unit(const s_param_unit *param, const char *name); + +#endif // !GTP_PARAM_UNIT_H_ From 27a5e993fff3387536467b1789a0aecebae30747 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 16 Dec 2024 17:05:28 +0100 Subject: [PATCH 39/45] Added GTP param_enum --- src_features/generic_tx_parser/gtp_field.c | 17 ++++ src_features/generic_tx_parser/gtp_field.h | 5 + .../generic_tx_parser/gtp_param_enum.c | 91 +++++++++++++++++++ .../generic_tx_parser/gtp_param_enum.h | 26 ++++++ 4 files changed, 139 insertions(+) create mode 100644 src_features/generic_tx_parser/gtp_param_enum.c create mode 100644 src_features/generic_tx_parser/gtp_param_enum.h diff --git a/src_features/generic_tx_parser/gtp_field.c b/src_features/generic_tx_parser/gtp_field.c index 7dceef4d8..09e5e6f50 100644 --- a/src_features/generic_tx_parser/gtp_field.c +++ b/src_features/generic_tx_parser/gtp_field.c @@ -29,6 +29,9 @@ typedef union { s_param_datetime_context datetime_ctx; s_param_duration_context duration_ctx; s_param_unit_context unit_ctx; +#ifdef HAVE_ENUM_VALUE + s_param_enum_context enum_ctx; +#endif } u_param_context; static bool handle_version(const s_tlv_data *data, s_field_ctx *context) { @@ -68,6 +71,9 @@ static bool handle_param_type(const s_tlv_data *data, s_field_ctx *context) { case PARAM_TYPE_DATETIME: case PARAM_TYPE_DURATION: case PARAM_TYPE_UNIT: +#ifdef HAVE_ENUM_VALUE + case PARAM_TYPE_ENUM: +#endif break; default: PRINTF("Error: Unsupported param type (%u)\n", context->field->param_type); @@ -120,6 +126,12 @@ static bool handle_param(const s_tlv_data *data, s_field_ctx *context) { handler = (f_tlv_data_handler) &handle_param_unit_struct; param_ctx.unit_ctx.param = &context->field->param_unit; break; +#ifdef HAVE_ENUM_VALUE + case PARAM_TYPE_ENUM: + handler = (f_tlv_data_handler) &handle_param_enum_struct; + param_ctx.enum_ctx.param = &context->field->param_enum; + break; +#endif default: return false; } @@ -204,6 +216,11 @@ bool format_field(const s_field *field) { case PARAM_TYPE_UNIT: ret = format_param_unit(&field->param_unit, field->name); break; +#ifdef HAVE_ENUM_VALUE + case PARAM_TYPE_ENUM: + ret = format_param_enum(&field->param_enum, field->name); + break; +#endif default: ret = false; } diff --git a/src_features/generic_tx_parser/gtp_field.h b/src_features/generic_tx_parser/gtp_field.h index db75e954b..cd608f742 100644 --- a/src_features/generic_tx_parser/gtp_field.h +++ b/src_features/generic_tx_parser/gtp_field.h @@ -11,6 +11,7 @@ #include "gtp_param_datetime.h" #include "gtp_param_duration.h" #include "gtp_param_unit.h" +#include "gtp_param_enum.h" typedef enum { PARAM_TYPE_RAW = 0, @@ -20,6 +21,7 @@ typedef enum { PARAM_TYPE_DATETIME, PARAM_TYPE_DURATION, PARAM_TYPE_UNIT, + PARAM_TYPE_ENUM, } e_param_type; typedef struct { @@ -36,6 +38,9 @@ typedef struct { s_param_datetime param_datetime; s_param_duration param_duration; s_param_unit param_unit; +#ifdef HAVE_ENUM_VALUE + s_param_enum param_enum; +#endif }; } s_field; diff --git a/src_features/generic_tx_parser/gtp_param_enum.c b/src_features/generic_tx_parser/gtp_param_enum.c new file mode 100644 index 000000000..15d90a2f4 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_enum.c @@ -0,0 +1,91 @@ +#ifdef HAVE_GENERIC_TX_PARSER +#ifdef HAVE_ENUM_VALUE + +#include "gtp_param_enum.h" +#include "network.h" +#include "enum_value.h" +#include "gtp_field_table.h" +#include "calldata.h" +#include "shared_context.h" + +enum { + TAG_VERSION = 0x00, + TAG_ID = 0x01, + TAG_VALUE = 0x02, +}; + +static bool handle_version(const s_tlv_data *data, s_param_enum_context *context) { + if (data->length != sizeof(context->param->version)) { + return false; + } + context->param->version = data->value[0]; + return true; +} + +static bool handle_id(const s_tlv_data *data, s_param_enum_context *context) { + if (data->length != sizeof(context->param->id)) { + return false; + } + context->param->id = data->value[0]; + return true; +} + +static bool handle_value(const s_tlv_data *data, s_param_enum_context *context) { + s_value_context ctx = {0}; + + ctx.value = &context->param->value; + explicit_bzero(ctx.value, sizeof(*ctx.value)); + return tlv_parse(data->value, data->length, (f_tlv_data_handler) &handle_value_struct, &ctx); +} + +bool handle_param_enum_struct(const s_tlv_data *data, s_param_enum_context *context) { + bool ret; + + switch (data->tag) { + case TAG_VERSION: + ret = handle_version(data, context); + break; + case TAG_ID: + ret = handle_id(data, context); + break; + case TAG_VALUE: + ret = handle_value(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + ret = false; + } + return ret; +} + +bool format_param_enum(const s_param_enum *param, const char *name) { + uint64_t chain_id; + s_parsed_value_collection collec; + const char *enum_name; + uint8_t value; + const uint8_t *selector; + + if (!value_get(¶m->value, &collec)) { + return false; + } + chain_id = get_tx_chain_id(); + for (int i = 0; i < collec.size; ++i) { + if (collec.value[i].length == 0) return false; + value = collec.value[i].ptr[collec.value[i].length - 1]; + if ((selector = calldata_get_selector()) == NULL) { + return false; + } + enum_name = get_matching_enum_name(&chain_id, + txContext.content->destination, + selector, + param->id, + value); + if (!add_to_field_table(PARAM_TYPE_ENUM, name, enum_name)) { + return false; + } + } + return true; +} + +#endif // HAVE_ENUM_VALUE +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/gtp_param_enum.h b/src_features/generic_tx_parser/gtp_param_enum.h new file mode 100644 index 000000000..2bfddaa45 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_enum.h @@ -0,0 +1,26 @@ +#ifndef GTP_PARAM_ENUM_H_ +#define GTP_PARAM_ENUM_H_ + +#ifdef HAVE_ENUM_VALUE + +#include +#include +#include "tlv.h" +#include "gtp_value.h" + +typedef struct { + uint8_t version; + uint8_t id; + s_value value; +} s_param_enum; + +typedef struct { + s_param_enum *param; +} s_param_enum_context; + +bool handle_param_enum_struct(const s_tlv_data *data, s_param_enum_context *context); +bool format_param_enum(const s_param_enum *param, const char *name); + +#endif + +#endif // !GTP_PARAM_ENUM_H_ From 3ac3e87bc2e686a2fea9535ea07ace0a74603205 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 16 Dec 2024 17:09:36 +0100 Subject: [PATCH 40/45] Added GTP param_trusted_name --- src_features/generic_tx_parser/gtp_field.c | 17 +++ src_features/generic_tx_parser/gtp_field.h | 5 + .../gtp_param_trusted_name.c | 111 ++++++++++++++++++ .../gtp_param_trusted_name.h | 31 +++++ 4 files changed, 164 insertions(+) create mode 100644 src_features/generic_tx_parser/gtp_param_trusted_name.c create mode 100644 src_features/generic_tx_parser/gtp_param_trusted_name.h diff --git a/src_features/generic_tx_parser/gtp_field.c b/src_features/generic_tx_parser/gtp_field.c index 09e5e6f50..9bec0c2f8 100644 --- a/src_features/generic_tx_parser/gtp_field.c +++ b/src_features/generic_tx_parser/gtp_field.c @@ -32,6 +32,9 @@ typedef union { #ifdef HAVE_ENUM_VALUE s_param_enum_context enum_ctx; #endif +#ifdef HAVE_TRUSTED_NAME + s_param_trusted_name_context trusted_name_ctx; +#endif } u_param_context; static bool handle_version(const s_tlv_data *data, s_field_ctx *context) { @@ -73,6 +76,9 @@ static bool handle_param_type(const s_tlv_data *data, s_field_ctx *context) { case PARAM_TYPE_UNIT: #ifdef HAVE_ENUM_VALUE case PARAM_TYPE_ENUM: +#endif +#ifdef HAVE_TRUSTED_NAME + case PARAM_TYPE_TRUSTED_NAME: #endif break; default: @@ -131,6 +137,12 @@ static bool handle_param(const s_tlv_data *data, s_field_ctx *context) { handler = (f_tlv_data_handler) &handle_param_enum_struct; param_ctx.enum_ctx.param = &context->field->param_enum; break; +#endif +#ifdef HAVE_TRUSTED_NAME + case PARAM_TYPE_TRUSTED_NAME: + handler = (f_tlv_data_handler) &handle_param_trusted_name_struct; + param_ctx.trusted_name_ctx.param = &context->field->param_trusted_name; + break; #endif default: return false; @@ -220,6 +232,11 @@ bool format_field(const s_field *field) { case PARAM_TYPE_ENUM: ret = format_param_enum(&field->param_enum, field->name); break; +#endif +#ifdef HAVE_TRUSTED_NAME + case PARAM_TYPE_TRUSTED_NAME: + ret = format_param_trusted_name(&field->param_trusted_name, field->name); + break; #endif default: ret = false; diff --git a/src_features/generic_tx_parser/gtp_field.h b/src_features/generic_tx_parser/gtp_field.h index cd608f742..2a5002f64 100644 --- a/src_features/generic_tx_parser/gtp_field.h +++ b/src_features/generic_tx_parser/gtp_field.h @@ -12,6 +12,7 @@ #include "gtp_param_duration.h" #include "gtp_param_unit.h" #include "gtp_param_enum.h" +#include "gtp_param_trusted_name.h" typedef enum { PARAM_TYPE_RAW = 0, @@ -22,6 +23,7 @@ typedef enum { PARAM_TYPE_DURATION, PARAM_TYPE_UNIT, PARAM_TYPE_ENUM, + PARAM_TYPE_TRUSTED_NAME, } e_param_type; typedef struct { @@ -40,6 +42,9 @@ typedef struct { s_param_unit param_unit; #ifdef HAVE_ENUM_VALUE s_param_enum param_enum; +#endif +#ifdef HAVE_TRUSTED_NAME + s_param_trusted_name param_trusted_name; #endif }; } s_field; diff --git a/src_features/generic_tx_parser/gtp_param_trusted_name.c b/src_features/generic_tx_parser/gtp_param_trusted_name.c new file mode 100644 index 000000000..5fffb9fe2 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_trusted_name.c @@ -0,0 +1,111 @@ +#ifdef HAVE_GENERIC_TX_PARSER +#ifdef HAVE_TRUSTED_NAME + +#include "gtp_param_trusted_name.h" +#include "network.h" +#include "trusted_name.h" +#include "gtp_field_table.h" +#include "utils.h" +#include "shared_context.h" + +enum { + TAG_VERSION = 0x00, + TAG_VALUE = 0x01, + TAG_TYPES = 0x02, + TAG_SOURCES = 0x03, +}; + +static bool handle_version(const s_tlv_data *data, s_param_trusted_name_context *context) { + if (data->length != sizeof(context->param->version)) { + return false; + } + context->param->version = data->value[0]; + return true; +} + +static bool handle_value(const s_tlv_data *data, s_param_trusted_name_context *context) { + s_value_context ctx = {0}; + + ctx.value = &context->param->value; + explicit_bzero(ctx.value, sizeof(*ctx.value)); + return tlv_parse(data->value, data->length, (f_tlv_data_handler) &handle_value_struct, &ctx); +} + +static bool handle_types(const s_tlv_data *data, s_param_trusted_name_context *context) { + if (data->length > sizeof(context->param->types)) { + return false; + } + memcpy(context->param->types, data->value, data->length); + context->param->type_count = data->length; + return true; +} + +static bool handle_sources(const s_tlv_data *data, s_param_trusted_name_context *context) { + if (data->length > sizeof(context->param->sources)) { + return false; + } + memcpy(context->param->sources, data->value, data->length); + context->param->source_count = data->length; + return true; +} + +bool handle_param_trusted_name_struct(const s_tlv_data *data, + s_param_trusted_name_context *context) { + bool ret; + + switch (data->tag) { + case TAG_VERSION: + ret = handle_version(data, context); + break; + case TAG_VALUE: + ret = handle_value(data, context); + break; + case TAG_TYPES: + ret = handle_types(data, context); + break; + case TAG_SOURCES: + ret = handle_sources(data, context); + break; + default: + PRINTF(TLV_TAG_ERROR_MSG, data->tag); + ret = false; + } + return ret; +} + +bool format_param_trusted_name(const s_param_trusted_name *param, const char *name) { + s_parsed_value_collection values; + char *buf = strings.tmp.tmp; + size_t buf_size = sizeof(strings.tmp.tmp); + uint64_t chain_id; + uint8_t addr[ADDRESS_LENGTH]; + const char *tname; + e_param_type param_type; + + if (!value_get(¶m->value, &values)) { + return false; + } + chain_id = get_tx_chain_id(); + for (int i = 0; i < values.size; ++i) { + buf_shrink_expand(values.value[i].ptr, values.value[i].length, addr, sizeof(addr)); + if ((tname = get_trusted_name(param->type_count, + param->types, + param->source_count, + param->sources, + &chain_id, + addr)) != NULL) { + strlcpy(buf, tname, buf_size); + param_type = PARAM_TYPE_TRUSTED_NAME; + } else { + getEthDisplayableAddress(addr, buf, buf_size, chainConfig->chainId); + param_type = PARAM_TYPE_RAW; + } + if (!add_to_field_table(param_type, name, buf)) { + return false; + } + } + return true; +} + +#endif // HAVE_TRUSTED_NAME +#endif // HAVE_GENERIC_TX_PARSER diff --git a/src_features/generic_tx_parser/gtp_param_trusted_name.h b/src_features/generic_tx_parser/gtp_param_trusted_name.h new file mode 100644 index 000000000..3232d7ad5 --- /dev/null +++ b/src_features/generic_tx_parser/gtp_param_trusted_name.h @@ -0,0 +1,31 @@ +#ifndef GTP_PARAM_TRUSTED_NAME_H_ +#define GTP_PARAM_TRUSTED_NAME_H_ + +#ifdef HAVE_TRUSTED_NAME + +#include +#include +#include "tlv.h" +#include "gtp_value.h" +#include "trusted_name.h" + +typedef struct { + uint8_t version; + s_value value; + uint8_t type_count; + e_name_type types[TN_TYPE_COUNT]; + uint8_t source_count; + e_name_source sources[TN_SOURCE_COUNT]; +} s_param_trusted_name; + +typedef struct { + s_param_trusted_name *param; +} s_param_trusted_name_context; + +bool handle_param_trusted_name_struct(const s_tlv_data *data, + s_param_trusted_name_context *context); +bool format_param_trusted_name(const s_param_trusted_name *param, const char *name); + +#endif + +#endif // !GTP_PARAM_TRUSTED_NAME_H_ From cdaef754334ca025c26d493918d0e0fc4e835c39 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 15 Nov 2024 11:19:30 +0100 Subject: [PATCH 41/45] Max fees & network formatting functions are now accessible from outside the signTx feature --- src_features/signTx/feature_signTx.h | 5 +++++ src_features/signTx/logic_signTx.c | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src_features/signTx/feature_signTx.h b/src_features/signTx/feature_signTx.h index 9f38e1e37..9f0af6371 100644 --- a/src_features/signTx/feature_signTx.h +++ b/src_features/signTx/feature_signTx.h @@ -38,5 +38,10 @@ void send_swap_error(uint8_t error_code, app_code_t app_code, const char *str1, uint16_t handle_parsing_status(parserStatus_e status); uint16_t get_public_key(uint8_t *out, uint8_t outLength); +bool max_transaction_fee_to_string(const txInt256_t *BEGasPrice, + const txInt256_t *BEGasLimit, + char *displayBuffer, + uint32_t displayBufferSize); +uint16_t get_network_as_string(char *out, size_t out_size); #endif // _SIGN_TX_H_ diff --git a/src_features/signTx/logic_signTx.c b/src_features/signTx/logic_signTx.c index 740bdbbfb..1f9085346 100644 --- a/src_features/signTx/logic_signTx.c +++ b/src_features/signTx/logic_signTx.c @@ -260,10 +260,10 @@ static void raw_fee_to_string(uint256_t *rawFee, char *displayBuffer, uint32_t d // Compute the fees, transform it to a string, prepend a ticker to it and copy everything to // `displayBuffer` output -static bool max_transaction_fee_to_string(const txInt256_t *BEGasPrice, - const txInt256_t *BEGasLimit, - char *displayBuffer, - uint32_t displayBufferSize) { +bool max_transaction_fee_to_string(const txInt256_t *BEGasPrice, + const txInt256_t *BEGasLimit, + char *displayBuffer, + uint32_t displayBufferSize) { // Use temporary variables to convert values to uint256_t uint256_t gasPrice = {0}; uint256_t gasLimit = {0}; @@ -287,7 +287,7 @@ static void nonce_to_string(const txInt256_t *nonce, char *out, size_t out_size) tostring256(&nonce_uint256, 10, out, out_size); } -static uint16_t get_network_as_string(char *out, size_t out_size) { +uint16_t get_network_as_string(char *out, size_t out_size) { uint64_t chain_id = get_tx_chain_id(); const char *name = get_network_name_from_chain_id(&chain_id); From f7960b5524f0200c868f3fb782c53e68383348e8 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 13 Nov 2024 19:16:55 +0100 Subject: [PATCH 42/45] BAGL GCS UI implementation --- src_bagl/ui_gcs.c | 438 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 437 insertions(+), 1 deletion(-) diff --git a/src_bagl/ui_gcs.c b/src_bagl/ui_gcs.c index 61f2deac6..ca0505bf7 100644 --- a/src_bagl/ui_gcs.c +++ b/src_bagl/ui_gcs.c @@ -1,6 +1,442 @@ #ifdef HAVE_GENERIC_TX_PARSER -void ui_gcs(void) { +#include "ux.h" +#include "gtp_field_table.h" +#include "gtp_tx_info.h" +#include "feature_signTx.h" +#include "ui_callbacks.h" +#include "network.h" +#include "apdu_constants.h" + +// forward declaration for BAGL step +static void review_contract_info(void); +static void review(int index); +static void dyn_prev(void); +static void dyn_next(void); +static void switch_pre_dyn(void); +static void switch_post_dyn(void); +static void prepare_review_title(void); +static void switch_network(bool forward); +static void network_prev(void); +static void network_next(void); + +static void format_network(void) { + if (get_network_as_string(strings.tmp.tmp, sizeof(strings.tmp.tmp)) != APDU_RESPONSE_OK) { + PRINTF("Error: Could not format the network!\n"); + } +} + +static void format_fees(void) { + if (max_transaction_fee_to_string(&tmpContent.txContent.gasprice, + &tmpContent.txContent.startgas, + strings.tmp.tmp, + sizeof(strings.tmp.tmp)) == false) { + PRINTF("Error: Could not format the max fees!\n"); + } +} + +// clang-format off +UX_STEP_NOCB_INIT( + ui_gcs_review_step, + pnn, + prepare_review_title(), + { + &C_icon_eye, + strings.tmp.tmp2, + strings.tmp.tmp, + } +); +UX_STEP_CB( + ui_gcs_contract_info_btn_step, + pbb, + review_contract_info(), + { + &C_icon_certificate, + "See contract", + "information", + } +); +UX_STEP_CB( + ui_gcs_back_step, + pb, + review(1), + { + &C_icon_back_x, + "Back", + } +); +UX_STEP_INIT( + ui_gcs_switch_pre_network_step, + NULL, + NULL, + { + switch_network(true); + } +); +UX_STEP_INIT( + ui_gcs_network_prev_step, + NULL, + NULL, + { + network_prev(); + } +); +UX_STEP_NOCB_INIT( + ui_gcs_network_step, + bnnn_paging, + format_network(), + { + .title = "Network", + .text = strings.tmp.tmp, + } +); +UX_STEP_INIT( + ui_gcs_network_next_step, + NULL, + NULL, + { + network_next(); + } +); +UX_STEP_INIT( + ui_gcs_switch_post_network_step, + NULL, + NULL, + { + switch_network(false); + } +); +UX_STEP_NOCB_INIT( + ui_gcs_fees_step, + bnnn_paging, + format_fees(), + { + .title = "Max fees", + .text = strings.tmp.tmp, + } +); +UX_STEP_CB( + ui_gcs_accept_step, + pb, + tx_ok_cb(), + { + &C_icon_validate_14, + "Sign transaction", + } +); +UX_STEP_CB( + ui_gcs_reject_step, + pb, + tx_cancel_cb(), + { + &C_icon_crossmark, + "Reject", + } +); + +UX_STEP_INIT( + ui_gcs_dyn_prev_step, + NULL, + NULL, + { + dyn_prev(); + } +); +UX_STEP_NOCB( + ui_gcs_dyn_step, + bnnn_paging, + { + .title = strings.tmp.tmp2, + .text = strings.tmp.tmp, + } +); +UX_STEP_INIT( + ui_gcs_dyn_next_step, + NULL, + NULL, + { + dyn_next(); + } +); + +UX_STEP_INIT( + ui_gcs_switch_pre_dyn_step, + NULL, + NULL, + { + switch_pre_dyn(); + } +); +UX_STEP_INIT( + ui_gcs_switch_post_dyn_step, + NULL, + NULL, + { + switch_post_dyn(); + } +); +// clang-format on + +UX_FLOW(ui_gcs_flow, + &ui_gcs_review_step, + &ui_gcs_contract_info_btn_step, + + // dynamic field handling + &ui_gcs_switch_pre_dyn_step, + &ui_gcs_dyn_prev_step, + &ui_gcs_dyn_step, + &ui_gcs_dyn_next_step, + &ui_gcs_switch_post_dyn_step, + + // network handling + &ui_gcs_switch_pre_network_step, + &ui_gcs_network_prev_step, + &ui_gcs_network_step, + &ui_gcs_network_next_step, + &ui_gcs_switch_post_network_step, + + &ui_gcs_fees_step, + &ui_gcs_accept_step, + &ui_gcs_reject_step); + +UX_FLOW(ui_gcs_contract_info_beg_flow, &ui_gcs_switch_pre_dyn_step); + +UX_FLOW(ui_gcs_dyn_beg_flow, &ui_gcs_dyn_step, &ui_gcs_dyn_next_step); + +UX_FLOW(ui_gcs_dyn_middle_flow, &ui_gcs_dyn_prev_step, &ui_gcs_dyn_step, &ui_gcs_dyn_next_step); + +UX_FLOW(ui_gcs_contract_info_end_flow, &ui_gcs_switch_post_dyn_step, &ui_gcs_back_step); + +typedef enum { + TOP_LEVEL, + TX_INFO, +} e_level; + +static e_level level; +static uint8_t dyn_idx; + +static void prepare_review_title(void) { + const char *op_type; + + snprintf(strings.tmp.tmp2, sizeof(strings.tmp.tmp2), "Review transaction to"); + if ((op_type = get_operation_type()) != NULL) { + snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "%s", op_type); + } +} + +static bool prepare_kv_info(uint8_t idx) { + bool found = false; + uint8_t count = 0; + const char *value; + + const char *value2; + if ((value = get_creator_legal_name()) != NULL) { + count += 1; + if (count == idx) { + snprintf(strings.tmp.tmp2, sizeof(strings.tmp.tmp2), "Contract owner"); + if ((value2 = get_creator_url()) != NULL) { + snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "%s\n%s", value, value2); + } else { + snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "%s", value); + } + found = true; + } + } + + if ((value = get_contract_name()) != NULL) { + count += 1; + if (count == idx) { + snprintf(strings.tmp.tmp2, sizeof(strings.tmp.tmp2), "Contract"); + snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "%s", value); + found = true; + } + } + + const uint8_t *addr = get_contract_addr(); + count += 1; + if (count == idx) { + snprintf(strings.tmp.tmp2, sizeof(strings.tmp.tmp2), "Contract address"); + if (!getEthDisplayableAddress((uint8_t *) addr, + strings.tmp.tmp, + sizeof(strings.tmp.tmp), + chainConfig->chainId)) { + return false; + } + found = true; + } + + if ((value = get_deploy_date()) != NULL) { + count += 1; + if (count == idx) { + snprintf(strings.tmp.tmp2, sizeof(strings.tmp.tmp2), "Deployed on"); + snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "%s", value); + found = true; + } + } + return found; +} + +static bool prepare_key_value(uint8_t idx) { + bool found = false; + s_field_table_entry field; + + switch (level) { + case TX_INFO: + found = prepare_kv_info(idx); + break; + case TOP_LEVEL: + if ((found = get_from_field_table(idx - 1, &field))) { + strncpy(strings.tmp.tmp2, field.key, sizeof(strings.tmp.tmp2)); + strncpy(strings.tmp.tmp, field.value, sizeof(strings.tmp.tmp)); + } + break; + default: + break; + } + return found; +} + +static void dyn_prev(void) { + const ux_flow_step_t *const *flow = NULL; + const ux_flow_step_t *step = NULL; + + dyn_idx -= 1; + if (prepare_key_value(dyn_idx)) { + // found + switch (level) { + case TX_INFO: + flow = (dyn_idx == 1) ? ui_gcs_dyn_beg_flow : ui_gcs_dyn_middle_flow; + break; + case TOP_LEVEL: + flow = ui_gcs_flow; + break; + default: + break; + } + step = &ui_gcs_dyn_step; + } else { + // not found + switch (level) { + case TX_INFO: + flow = ui_gcs_contract_info_end_flow; + step = &ui_gcs_back_step; + break; + case TOP_LEVEL: + flow = ui_gcs_flow; + step = &ui_gcs_contract_info_btn_step; + break; + default: + break; + } + } + ux_flow_init(0, flow, step); +} + +static void dyn_next(void) { + const ux_flow_step_t *const *flow = NULL; + const ux_flow_step_t *step = NULL; + + dyn_idx += 1; + if (prepare_key_value(dyn_idx)) { + // found + switch (level) { + case TX_INFO: + flow = (dyn_idx == 1) ? ui_gcs_dyn_beg_flow : ui_gcs_dyn_middle_flow; + step = &ui_gcs_dyn_step; + break; + case TOP_LEVEL: + flow = ui_gcs_flow; + step = &ui_gcs_dyn_step; + break; + default: + break; + } + } else { + // not found + dyn_idx -= 1; + switch (level) { + case TX_INFO: + flow = ui_gcs_contract_info_end_flow; + step = &ui_gcs_back_step; + break; + case TOP_LEVEL: + flow = ui_gcs_flow; + step = &ui_gcs_switch_pre_network_step; + break; + default: + break; + } + } + ux_flow_init(0, flow, step); +} + +static void switch_pre_dyn(void) { + ux_flow_init(0, ui_gcs_flow, &ui_gcs_dyn_next_step); +} + +static void switch_post_dyn(void) { + const ux_flow_step_t *const *flow = NULL; + const ux_flow_step_t *step = NULL; + + switch (level) { + case TX_INFO: + flow = ui_gcs_dyn_middle_flow; + step = &ui_gcs_dyn_step; + break; + case TOP_LEVEL: + if (dyn_idx == 0) { + // no field has been shown + step = &ui_gcs_contract_info_btn_step; + } else { + // artificially lower the index then ask for the next one + dyn_idx -= 1; + step = &ui_gcs_dyn_next_step; + } + flow = ui_gcs_flow; + break; + default: + break; + } + ux_flow_init(0, flow, step); +} + +static void network_prev(void) { + ux_flow_init(0, ui_gcs_flow, &ui_gcs_switch_post_dyn_step); +} + +static void network_next(void) { + ux_flow_init(0, ui_gcs_flow, &ui_gcs_fees_step); +} + +static void switch_network(bool forward) { + uint64_t chain_id = get_tx_chain_id(); + + if (chain_id != chainConfig->chainId) { + ux_flow_init(0, ui_gcs_flow, &ui_gcs_network_step); + } else { + if (forward) { + network_next(); + } else { + network_prev(); + } + } +} + +static void review_contract_info(void) { + dyn_idx = 0; + level = TX_INFO; + ux_flow_init(0, ui_gcs_dyn_middle_flow, &ui_gcs_dyn_next_step); +} + +static void review(int index) { + dyn_idx = 0; + level = TOP_LEVEL; + ux_flow_init(0, ui_gcs_flow, ui_gcs_flow[index]); +} + +bool ui_gcs(void) { + review(0); + return true; } void ui_gcs_cleanup(void) { From 04c6ce6c9961be7463c78e714fc2a73d82695161 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 22 Nov 2024 16:19:57 +0100 Subject: [PATCH 43/45] NBGL GCS UI implementation --- glyphs/review_info_button.gif | Bin 0 -> 538 bytes src_nbgl/ui_gcs.c | 228 +++++++++++++++++++++++++++++++++- 2 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 glyphs/review_info_button.gif diff --git a/glyphs/review_info_button.gif b/glyphs/review_info_button.gif new file mode 100644 index 0000000000000000000000000000000000000000..82b19e717272c1cd2ac5e439a001232cf55a2be0 GIT binary patch literal 538 zcmZ?wbhEHbbYO5`_{_lo1oidx^XJch@ZiCdCr@6#e*NXkmtVhr{r~?TB!>YMf3kqZ z6#sL3<|d}6`X;7lCTH`dC+6lR+8XGYn1UdK4oD5i1_qXY0w+CJ&pkfHsqH()^p<4YsSxK zYQ8Z~y3`Z(M_SEOLAjxoN6hKQo70X>t!aNH1734Eo1eI%+Y-^kYht%b{OlH`N2h!| zET^d0-3fmlw>3lOti)xG%dxT<_wLNubBXbc_hgyWdy^f6QZ5*LH2Cx9TiT13ejk5_ zxf@JgiA0sniMktD>a=F9D2wS*mN1@c!m|!|$$Wp46Vj>byJjPodH?NLp~;n9n_u&J z?OS>6NNLd0$u65p|7=)!!=;e%SP;(!g=9CwB%gwRHf5h!xSm)|ik|Ruqk4OrsrRCw V%7ed}Iks&J+aS=p*pG$58USU+?wbGr literal 0 HcmV?d00001 diff --git a/src_nbgl/ui_gcs.c b/src_nbgl/ui_gcs.c index 61f2deac6..8f842837d 100644 --- a/src_nbgl/ui_gcs.c +++ b/src_nbgl/ui_gcs.c @@ -1,9 +1,235 @@ #ifdef HAVE_GENERIC_TX_PARSER -void ui_gcs(void) { +#include +#include "ui_nbgl.h" +#include "gtp_tx_info.h" +#include "gtp_field_table.h" +#include "mem.h" +#include "mem_utils.h" +#include "network.h" +#include "ui_callbacks.h" +#include "feature_signTx.h" +#include "apdu_constants.h" + +static nbgl_layoutTagValueList_t g_pair_list; +static size_t g_alloc_size; + +static void review_choice(bool confirm) { + if (confirm) { + io_seproxyhal_touch_tx_ok(); + nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_SIGNED, ui_idle); + } else { + io_seproxyhal_touch_tx_cancel(); + nbgl_useCaseReviewStatus(STATUS_TYPE_TRANSACTION_REJECTED, ui_idle); + } +} + +static char *_strdup(const char *src) { + char *dst; + size_t length = strlen(src) + 1; + + if ((dst = mem_alloc(length)) != NULL) { + memmove(dst, src, length); + } + return dst; +} + +static bool cleanup_on_error(const void *mem_before) { + mem_dealloc(mem_alloc(0) - mem_before); + return false; +} + +#define MAX_INFO_COUNT 3 + +static bool prepare_infos(nbgl_contentInfoList_t *infos) { + char *tmp_buf = strings.tmp.tmp; + size_t tmp_buf_size = sizeof(strings.tmp.tmp); + size_t off; + uint8_t count = 0; + const char **keys; + const char **values; + nbgl_contentValueExt_t *extensions; + const char *value; + int contract_idx = -1; + + if (((keys = mem_alloc_and_align(sizeof(*keys) * MAX_INFO_COUNT, __alignof__(*keys))) == + NULL) || + ((values = mem_alloc_and_align(sizeof(*values) * MAX_INFO_COUNT, __alignof__(*values))) == + NULL)) { + return false; + } + if ((value = get_creator_legal_name()) != NULL) { + snprintf(tmp_buf, tmp_buf_size, "Contract owner"); + if ((keys[count] = _strdup(tmp_buf)) == NULL) { + return false; + } + snprintf(tmp_buf, tmp_buf_size, "%s", value); + if ((value = get_creator_url()) != NULL) { + off = strlen(tmp_buf); + snprintf(tmp_buf + off, tmp_buf_size - off, "\n%s", value); + } + if ((values[count] = _strdup(tmp_buf)) == NULL) { + return false; + } + count += 1; + } + + if ((value = get_contract_name()) != NULL) { + snprintf(tmp_buf, tmp_buf_size, "Contract"); + if ((keys[count] = _strdup(tmp_buf)) == NULL) { + return false; + } + snprintf(tmp_buf, tmp_buf_size, "%s", value); + if ((values[count] = _strdup(tmp_buf)) == NULL) { + return false; + } + contract_idx = count; + count += 1; + } + + if ((value = get_deploy_date()) != NULL) { + snprintf(tmp_buf, tmp_buf_size, "Deployed on"); + if ((keys[count] = _strdup(tmp_buf)) == NULL) { + return false; + } + snprintf(tmp_buf, tmp_buf_size, "%s", value); + if ((values[count] = _strdup(tmp_buf)) == NULL) { + return false; + } + count += 1; + } + + if ((extensions = mem_alloc_and_align(sizeof(*extensions) * count, __alignof__(*extensions))) == + NULL) { + return false; + } + explicit_bzero(extensions, sizeof(*extensions) * count); + + if (contract_idx != -1) { + if (!getEthDisplayableAddress((uint8_t *) get_contract_addr(), + tmp_buf, + tmp_buf_size, + chainConfig->chainId)) { + return false; + } + if ((extensions[contract_idx].title = _strdup(tmp_buf)) == NULL) { + return false; + } + // Etherscan only for mainnet + if (get_tx_chain_id() == ETHEREUM_MAINNET_CHAINID) { + if ((extensions[contract_idx].explanation = _strdup("Scan to view on Etherscan")) == + NULL) { + return false; + } + snprintf(tmp_buf, + tmp_buf_size, + "https://etherscan.io/address/%s", + extensions[contract_idx].title); + if ((extensions[contract_idx].fullValue = _strdup(tmp_buf)) == NULL) { + return false; + } + } else { + extensions[contract_idx].fullValue = extensions[contract_idx].title; + } + extensions[contract_idx].aliasType = QR_CODE_ALIAS; + } + + infos->nbInfos = count; + infos->infoTypes = keys; + infos->infoContents = values; + infos->infoExtensions = extensions; + infos->withExtensions = true; + return true; +} + +bool ui_gcs(void) { + char *tmp_buf = strings.tmp.tmp; + size_t tmp_buf_size = sizeof(strings.tmp.tmp); + size_t off; + const char *review_title; + const char *sign_title; + nbgl_contentTagValue_t *pairs; + s_field_table_entry entry; + bool show_network; + nbgl_tipBox_t tip_box; + const void *mem_before = mem_alloc(0); + + snprintf(tmp_buf, tmp_buf_size, "Review transaction to %s", get_operation_type()); + if ((review_title = _strdup(tmp_buf)) == NULL) { + return cleanup_on_error(mem_before); + } + snprintf(tmp_buf, tmp_buf_size, "Sign transaction to %s?", get_operation_type()); + if ((sign_title = _strdup(tmp_buf)) == NULL) { + return cleanup_on_error(mem_before); + } + explicit_bzero(&tip_box, sizeof(tip_box)); + tip_box.icon = &C_review_info_button; + tip_box.text = NULL; + tip_box.modalTitle = "Contract information"; + tip_box.type = INFOS_LIST; + if (!prepare_infos(&tip_box.infos)) { + return cleanup_on_error(mem_before); + } + snprintf(tmp_buf, tmp_buf_size, "Interaction with a\nsmart contract"); + off = strlen(tmp_buf); + if (get_creator_name()) { + snprintf(tmp_buf + off, tmp_buf_size - off, " from:\n%s", get_creator_name()); + } + if ((tip_box.text = _strdup(tmp_buf)) == NULL) { + return cleanup_on_error(mem_before); + } + + g_pair_list.nbPairs = field_table_size() + 1; + show_network = get_tx_chain_id() != chainConfig->chainId; + if (show_network) { + g_pair_list.nbPairs += 1; + } + if ((pairs = mem_alloc_and_align(sizeof(*pairs) * g_pair_list.nbPairs, __alignof__(*pairs))) == + NULL) { + return cleanup_on_error(mem_before); + } + explicit_bzero(pairs, sizeof(*pairs) * g_pair_list.nbPairs); + + for (int i = 0; i < (int) field_table_size(); ++i) { + if (!get_from_field_table(i, &entry)) { + return cleanup_on_error(mem_before); + } + pairs[i].item = entry.key; + pairs[i].value = entry.value; + } + + if (show_network) { + pairs[g_pair_list.nbPairs - 2].item = _strdup("Network"); + if (get_network_as_string(tmp_buf, tmp_buf_size) != APDU_RESPONSE_OK) { + return cleanup_on_error(mem_before); + } + pairs[g_pair_list.nbPairs - 2].value = _strdup(tmp_buf); + } + + pairs[g_pair_list.nbPairs - 1].item = _strdup("Max fees"); + if (max_transaction_fee_to_string(&tmpContent.txContent.gasprice, + &tmpContent.txContent.startgas, + tmp_buf, + tmp_buf_size) == false) { + PRINTF("Error: Could not format the max fees!\n"); + } + pairs[g_pair_list.nbPairs - 1].value = _strdup(tmp_buf); + + g_pair_list.pairs = pairs; + nbgl_useCaseAdvancedReview(TYPE_TRANSACTION, + &g_pair_list, + get_tx_icon(), + review_title, + NULL, + sign_title, + &tip_box, + review_choice); + g_alloc_size = mem_alloc(0) - mem_before; + return true; } void ui_gcs_cleanup(void) { + mem_dealloc(g_alloc_size); } #endif // HAVE_GENERIC_TX_PARSER From ee9fd30e6db332b31fea2044477af585d980567c Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 5 Nov 2024 18:02:17 +0100 Subject: [PATCH 44/45] Ragger tests - NFT (ERC-1155) - Poap - 1inch --- tests/ragger/abis/1inch.abi.json | 2062 +++++++++++++++++ tests/ragger/abis/poap.abi.json | 359 +++ .../snapshots/flex/test_1inch/00000.png | Bin 0 -> 13453 bytes .../snapshots/flex/test_1inch/00001.png | Bin 0 -> 18725 bytes .../snapshots/flex/test_1inch/00002.png | Bin 0 -> 6964 bytes .../snapshots/flex/test_1inch/00003.png | Bin 0 -> 11536 bytes .../snapshots/flex/test_1inch/00004.png | Bin 0 -> 6357 bytes .../snapshots/flex/test_1inch/00005.png | Bin 0 -> 12445 bytes .../ragger/snapshots/flex/test_nft/00000.png | Bin 0 -> 13848 bytes .../ragger/snapshots/flex/test_nft/00001.png | Bin 0 -> 19107 bytes .../ragger/snapshots/flex/test_nft/00002.png | Bin 0 -> 20857 bytes .../ragger/snapshots/flex/test_nft/00003.png | Bin 0 -> 9953 bytes .../ragger/snapshots/flex/test_nft/00004.png | Bin 0 -> 10360 bytes .../ragger/snapshots/flex/test_nft/00005.png | Bin 0 -> 12650 bytes .../ragger/snapshots/flex/test_nft/00006.png | Bin 0 -> 6357 bytes .../ragger/snapshots/flex/test_nft/00007.png | Bin 0 -> 12445 bytes .../ragger/snapshots/flex/test_poap/00000.png | Bin 0 -> 13898 bytes .../ragger/snapshots/flex/test_poap/00001.png | Bin 0 -> 17315 bytes .../ragger/snapshots/flex/test_poap/00002.png | Bin 0 -> 10206 bytes .../ragger/snapshots/flex/test_poap/00003.png | Bin 0 -> 28312 bytes .../ragger/snapshots/flex/test_poap/00004.png | Bin 0 -> 11569 bytes .../ragger/snapshots/flex/test_poap/00005.png | Bin 0 -> 6357 bytes .../ragger/snapshots/flex/test_poap/00006.png | Bin 0 -> 12445 bytes .../snapshots/nanosp/test_1inch/00000.png | Bin 0 -> 478 bytes .../snapshots/nanosp/test_1inch/00001.png | Bin 0 -> 530 bytes .../snapshots/nanosp/test_1inch/00002.png | Bin 0 -> 756 bytes .../snapshots/nanosp/test_1inch/00003.png | Bin 0 -> 360 bytes .../snapshots/nanosp/test_1inch/00004.png | Bin 0 -> 450 bytes .../snapshots/nanosp/test_1inch/00005.png | Bin 0 -> 432 bytes .../snapshots/nanosp/test_1inch/00006.png | Bin 0 -> 437 bytes .../snapshots/nanosp/test_1inch/00007.png | Bin 0 -> 382 bytes .../snapshots/nanosp/test_nft/00000.png | Bin 0 -> 567 bytes .../snapshots/nanosp/test_nft/00001.png | Bin 0 -> 530 bytes .../snapshots/nanosp/test_nft/00002.png | Bin 0 -> 702 bytes .../snapshots/nanosp/test_nft/00003.png | Bin 0 -> 344 bytes .../snapshots/nanosp/test_nft/00004.png | Bin 0 -> 557 bytes .../snapshots/nanosp/test_nft/00005.png | Bin 0 -> 562 bytes .../snapshots/nanosp/test_nft/00006.png | Bin 0 -> 622 bytes .../snapshots/nanosp/test_nft/00007.png | Bin 0 -> 651 bytes .../snapshots/nanosp/test_nft/00008.png | Bin 0 -> 289 bytes .../snapshots/nanosp/test_nft/00009.png | Bin 0 -> 303 bytes .../snapshots/nanosp/test_nft/00010.png | Bin 0 -> 301 bytes .../snapshots/nanosp/test_nft/00011.png | Bin 0 -> 302 bytes .../snapshots/nanosp/test_nft/00012.png | Bin 0 -> 430 bytes .../snapshots/nanosp/test_nft/00013.png | Bin 0 -> 432 bytes .../snapshots/nanosp/test_nft/00014.png | Bin 0 -> 437 bytes .../snapshots/nanosp/test_nft/00015.png | Bin 0 -> 382 bytes .../snapshots/nanosp/test_poap/00000.png | Bin 0 -> 511 bytes .../snapshots/nanosp/test_poap/00001.png | Bin 0 -> 530 bytes .../snapshots/nanosp/test_poap/00002.png | Bin 0 -> 361 bytes .../snapshots/nanosp/test_poap/00003.png | Bin 0 -> 393 bytes .../snapshots/nanosp/test_poap/00004.png | Bin 0 -> 754 bytes .../snapshots/nanosp/test_poap/00005.png | Bin 0 -> 629 bytes .../snapshots/nanosp/test_poap/00006.png | Bin 0 -> 925 bytes .../snapshots/nanosp/test_poap/00007.png | Bin 0 -> 948 bytes .../snapshots/nanosp/test_poap/00008.png | Bin 0 -> 632 bytes .../snapshots/nanosp/test_poap/00009.png | Bin 0 -> 432 bytes .../snapshots/nanosp/test_poap/00010.png | Bin 0 -> 437 bytes .../snapshots/nanosp/test_poap/00011.png | Bin 0 -> 382 bytes .../snapshots/nanox/test_1inch/00000.png | Bin 0 -> 478 bytes .../snapshots/nanox/test_1inch/00001.png | Bin 0 -> 530 bytes .../snapshots/nanox/test_1inch/00002.png | Bin 0 -> 756 bytes .../snapshots/nanox/test_1inch/00003.png | Bin 0 -> 360 bytes .../snapshots/nanox/test_1inch/00004.png | Bin 0 -> 450 bytes .../snapshots/nanox/test_1inch/00005.png | Bin 0 -> 432 bytes .../snapshots/nanox/test_1inch/00006.png | Bin 0 -> 437 bytes .../snapshots/nanox/test_1inch/00007.png | Bin 0 -> 382 bytes .../ragger/snapshots/nanox/test_nft/00000.png | Bin 0 -> 567 bytes .../ragger/snapshots/nanox/test_nft/00001.png | Bin 0 -> 530 bytes .../ragger/snapshots/nanox/test_nft/00002.png | Bin 0 -> 702 bytes .../ragger/snapshots/nanox/test_nft/00003.png | Bin 0 -> 344 bytes .../ragger/snapshots/nanox/test_nft/00004.png | Bin 0 -> 557 bytes .../ragger/snapshots/nanox/test_nft/00005.png | Bin 0 -> 562 bytes .../ragger/snapshots/nanox/test_nft/00006.png | Bin 0 -> 622 bytes .../ragger/snapshots/nanox/test_nft/00007.png | Bin 0 -> 651 bytes .../ragger/snapshots/nanox/test_nft/00008.png | Bin 0 -> 289 bytes .../ragger/snapshots/nanox/test_nft/00009.png | Bin 0 -> 303 bytes .../ragger/snapshots/nanox/test_nft/00010.png | Bin 0 -> 301 bytes .../ragger/snapshots/nanox/test_nft/00011.png | Bin 0 -> 302 bytes .../ragger/snapshots/nanox/test_nft/00012.png | Bin 0 -> 430 bytes .../ragger/snapshots/nanox/test_nft/00013.png | Bin 0 -> 432 bytes .../ragger/snapshots/nanox/test_nft/00014.png | Bin 0 -> 437 bytes .../ragger/snapshots/nanox/test_nft/00015.png | Bin 0 -> 382 bytes .../snapshots/nanox/test_poap/00000.png | Bin 0 -> 511 bytes .../snapshots/nanox/test_poap/00001.png | Bin 0 -> 530 bytes .../snapshots/nanox/test_poap/00002.png | Bin 0 -> 361 bytes .../snapshots/nanox/test_poap/00003.png | Bin 0 -> 393 bytes .../snapshots/nanox/test_poap/00004.png | Bin 0 -> 754 bytes .../snapshots/nanox/test_poap/00005.png | Bin 0 -> 629 bytes .../snapshots/nanox/test_poap/00006.png | Bin 0 -> 925 bytes .../snapshots/nanox/test_poap/00007.png | Bin 0 -> 948 bytes .../snapshots/nanox/test_poap/00008.png | Bin 0 -> 632 bytes .../snapshots/nanox/test_poap/00009.png | Bin 0 -> 432 bytes .../snapshots/nanox/test_poap/00010.png | Bin 0 -> 437 bytes .../snapshots/nanox/test_poap/00011.png | Bin 0 -> 382 bytes .../snapshots/stax/test_1inch/00000.png | Bin 0 -> 12478 bytes .../snapshots/stax/test_1inch/00001.png | Bin 0 -> 18763 bytes .../snapshots/stax/test_1inch/00002.png | Bin 0 -> 10775 bytes .../snapshots/stax/test_1inch/00003.png | Bin 0 -> 6218 bytes .../snapshots/stax/test_1inch/00004.png | Bin 0 -> 10991 bytes .../ragger/snapshots/stax/test_nft/00000.png | Bin 0 -> 12738 bytes .../ragger/snapshots/stax/test_nft/00001.png | Bin 0 -> 21556 bytes .../ragger/snapshots/stax/test_nft/00002.png | Bin 0 -> 17882 bytes .../ragger/snapshots/stax/test_nft/00003.png | Bin 0 -> 10580 bytes .../ragger/snapshots/stax/test_nft/00004.png | Bin 0 -> 11722 bytes .../ragger/snapshots/stax/test_nft/00005.png | Bin 0 -> 6218 bytes .../ragger/snapshots/stax/test_nft/00006.png | Bin 0 -> 10991 bytes .../ragger/snapshots/stax/test_poap/00000.png | Bin 0 -> 12769 bytes .../ragger/snapshots/stax/test_poap/00001.png | Bin 0 -> 19955 bytes .../ragger/snapshots/stax/test_poap/00002.png | Bin 0 -> 25661 bytes .../ragger/snapshots/stax/test_poap/00003.png | Bin 0 -> 10926 bytes .../ragger/snapshots/stax/test_poap/00004.png | Bin 0 -> 6218 bytes .../ragger/snapshots/stax/test_poap/00005.png | Bin 0 -> 10991 bytes tests/ragger/test_gcs.py | 544 +++++ 114 files changed, 2965 insertions(+) create mode 100644 tests/ragger/abis/1inch.abi.json create mode 100644 tests/ragger/abis/poap.abi.json create mode 100644 tests/ragger/snapshots/flex/test_1inch/00000.png create mode 100644 tests/ragger/snapshots/flex/test_1inch/00001.png create mode 100644 tests/ragger/snapshots/flex/test_1inch/00002.png create mode 100644 tests/ragger/snapshots/flex/test_1inch/00003.png create mode 100644 tests/ragger/snapshots/flex/test_1inch/00004.png create mode 100644 tests/ragger/snapshots/flex/test_1inch/00005.png create mode 100644 tests/ragger/snapshots/flex/test_nft/00000.png create mode 100644 tests/ragger/snapshots/flex/test_nft/00001.png create mode 100644 tests/ragger/snapshots/flex/test_nft/00002.png create mode 100644 tests/ragger/snapshots/flex/test_nft/00003.png create mode 100644 tests/ragger/snapshots/flex/test_nft/00004.png create mode 100644 tests/ragger/snapshots/flex/test_nft/00005.png create mode 100644 tests/ragger/snapshots/flex/test_nft/00006.png create mode 100644 tests/ragger/snapshots/flex/test_nft/00007.png create mode 100644 tests/ragger/snapshots/flex/test_poap/00000.png create mode 100644 tests/ragger/snapshots/flex/test_poap/00001.png create mode 100644 tests/ragger/snapshots/flex/test_poap/00002.png create mode 100644 tests/ragger/snapshots/flex/test_poap/00003.png create mode 100644 tests/ragger/snapshots/flex/test_poap/00004.png create mode 100644 tests/ragger/snapshots/flex/test_poap/00005.png create mode 100644 tests/ragger/snapshots/flex/test_poap/00006.png create mode 100644 tests/ragger/snapshots/nanosp/test_1inch/00000.png create mode 100644 tests/ragger/snapshots/nanosp/test_1inch/00001.png create mode 100644 tests/ragger/snapshots/nanosp/test_1inch/00002.png create mode 100644 tests/ragger/snapshots/nanosp/test_1inch/00003.png create mode 100644 tests/ragger/snapshots/nanosp/test_1inch/00004.png create mode 100644 tests/ragger/snapshots/nanosp/test_1inch/00005.png create mode 100644 tests/ragger/snapshots/nanosp/test_1inch/00006.png create mode 100644 tests/ragger/snapshots/nanosp/test_1inch/00007.png create mode 100644 tests/ragger/snapshots/nanosp/test_nft/00000.png create mode 100644 tests/ragger/snapshots/nanosp/test_nft/00001.png create mode 100644 tests/ragger/snapshots/nanosp/test_nft/00002.png create mode 100644 tests/ragger/snapshots/nanosp/test_nft/00003.png create mode 100644 tests/ragger/snapshots/nanosp/test_nft/00004.png create mode 100644 tests/ragger/snapshots/nanosp/test_nft/00005.png create mode 100644 tests/ragger/snapshots/nanosp/test_nft/00006.png create mode 100644 tests/ragger/snapshots/nanosp/test_nft/00007.png create mode 100644 tests/ragger/snapshots/nanosp/test_nft/00008.png create mode 100644 tests/ragger/snapshots/nanosp/test_nft/00009.png create mode 100644 tests/ragger/snapshots/nanosp/test_nft/00010.png create mode 100644 tests/ragger/snapshots/nanosp/test_nft/00011.png create mode 100644 tests/ragger/snapshots/nanosp/test_nft/00012.png create mode 100644 tests/ragger/snapshots/nanosp/test_nft/00013.png create mode 100644 tests/ragger/snapshots/nanosp/test_nft/00014.png create mode 100644 tests/ragger/snapshots/nanosp/test_nft/00015.png create mode 100644 tests/ragger/snapshots/nanosp/test_poap/00000.png create mode 100644 tests/ragger/snapshots/nanosp/test_poap/00001.png create mode 100644 tests/ragger/snapshots/nanosp/test_poap/00002.png create mode 100644 tests/ragger/snapshots/nanosp/test_poap/00003.png create mode 100644 tests/ragger/snapshots/nanosp/test_poap/00004.png create mode 100644 tests/ragger/snapshots/nanosp/test_poap/00005.png create mode 100644 tests/ragger/snapshots/nanosp/test_poap/00006.png create mode 100644 tests/ragger/snapshots/nanosp/test_poap/00007.png create mode 100644 tests/ragger/snapshots/nanosp/test_poap/00008.png create mode 100644 tests/ragger/snapshots/nanosp/test_poap/00009.png create mode 100644 tests/ragger/snapshots/nanosp/test_poap/00010.png create mode 100644 tests/ragger/snapshots/nanosp/test_poap/00011.png create mode 100644 tests/ragger/snapshots/nanox/test_1inch/00000.png create mode 100644 tests/ragger/snapshots/nanox/test_1inch/00001.png create mode 100644 tests/ragger/snapshots/nanox/test_1inch/00002.png create mode 100644 tests/ragger/snapshots/nanox/test_1inch/00003.png create mode 100644 tests/ragger/snapshots/nanox/test_1inch/00004.png create mode 100644 tests/ragger/snapshots/nanox/test_1inch/00005.png create mode 100644 tests/ragger/snapshots/nanox/test_1inch/00006.png create mode 100644 tests/ragger/snapshots/nanox/test_1inch/00007.png create mode 100644 tests/ragger/snapshots/nanox/test_nft/00000.png create mode 100644 tests/ragger/snapshots/nanox/test_nft/00001.png create mode 100644 tests/ragger/snapshots/nanox/test_nft/00002.png create mode 100644 tests/ragger/snapshots/nanox/test_nft/00003.png create mode 100644 tests/ragger/snapshots/nanox/test_nft/00004.png create mode 100644 tests/ragger/snapshots/nanox/test_nft/00005.png create mode 100644 tests/ragger/snapshots/nanox/test_nft/00006.png create mode 100644 tests/ragger/snapshots/nanox/test_nft/00007.png create mode 100644 tests/ragger/snapshots/nanox/test_nft/00008.png create mode 100644 tests/ragger/snapshots/nanox/test_nft/00009.png create mode 100644 tests/ragger/snapshots/nanox/test_nft/00010.png create mode 100644 tests/ragger/snapshots/nanox/test_nft/00011.png create mode 100644 tests/ragger/snapshots/nanox/test_nft/00012.png create mode 100644 tests/ragger/snapshots/nanox/test_nft/00013.png create mode 100644 tests/ragger/snapshots/nanox/test_nft/00014.png create mode 100644 tests/ragger/snapshots/nanox/test_nft/00015.png create mode 100644 tests/ragger/snapshots/nanox/test_poap/00000.png create mode 100644 tests/ragger/snapshots/nanox/test_poap/00001.png create mode 100644 tests/ragger/snapshots/nanox/test_poap/00002.png create mode 100644 tests/ragger/snapshots/nanox/test_poap/00003.png create mode 100644 tests/ragger/snapshots/nanox/test_poap/00004.png create mode 100644 tests/ragger/snapshots/nanox/test_poap/00005.png create mode 100644 tests/ragger/snapshots/nanox/test_poap/00006.png create mode 100644 tests/ragger/snapshots/nanox/test_poap/00007.png create mode 100644 tests/ragger/snapshots/nanox/test_poap/00008.png create mode 100644 tests/ragger/snapshots/nanox/test_poap/00009.png create mode 100644 tests/ragger/snapshots/nanox/test_poap/00010.png create mode 100644 tests/ragger/snapshots/nanox/test_poap/00011.png create mode 100644 tests/ragger/snapshots/stax/test_1inch/00000.png create mode 100644 tests/ragger/snapshots/stax/test_1inch/00001.png create mode 100644 tests/ragger/snapshots/stax/test_1inch/00002.png create mode 100644 tests/ragger/snapshots/stax/test_1inch/00003.png create mode 100644 tests/ragger/snapshots/stax/test_1inch/00004.png create mode 100644 tests/ragger/snapshots/stax/test_nft/00000.png create mode 100644 tests/ragger/snapshots/stax/test_nft/00001.png create mode 100644 tests/ragger/snapshots/stax/test_nft/00002.png create mode 100644 tests/ragger/snapshots/stax/test_nft/00003.png create mode 100644 tests/ragger/snapshots/stax/test_nft/00004.png create mode 100644 tests/ragger/snapshots/stax/test_nft/00005.png create mode 100644 tests/ragger/snapshots/stax/test_nft/00006.png create mode 100644 tests/ragger/snapshots/stax/test_poap/00000.png create mode 100644 tests/ragger/snapshots/stax/test_poap/00001.png create mode 100644 tests/ragger/snapshots/stax/test_poap/00002.png create mode 100644 tests/ragger/snapshots/stax/test_poap/00003.png create mode 100644 tests/ragger/snapshots/stax/test_poap/00004.png create mode 100644 tests/ragger/snapshots/stax/test_poap/00005.png create mode 100644 tests/ragger/test_gcs.py diff --git a/tests/ragger/abis/1inch.abi.json b/tests/ragger/abis/1inch.abi.json new file mode 100644 index 000000000..7deb4b632 --- /dev/null +++ b/tests/ragger/abis/1inch.abi.json @@ -0,0 +1,2062 @@ +[ + { + "inputs" : [ + { + "internalType" : "contract IWETH", + "name" : "weth", + "type" : "address" + } + ], + "stateMutability" : "nonpayable", + "type" : "constructor" + }, + { + "inputs" : [], + "name" : "AdvanceEpochFailed", + "type" : "error" + }, + { + "inputs" : [], + "name" : "ArbitraryStaticCallFailed", + "type" : "error" + }, + { + "inputs" : [], + "name" : "BadCurveSwapSelector", + "type" : "error" + }, + { + "inputs" : [], + "name" : "BadPool", + "type" : "error" + }, + { + "inputs" : [], + "name" : "BadSignature", + "type" : "error" + }, + { + "inputs" : [], + "name" : "BitInvalidatedOrder", + "type" : "error" + }, + { + "inputs" : [], + "name" : "ETHTransferFailed", + "type" : "error" + }, + { + "inputs" : [], + "name" : "ETHTransferFailed", + "type" : "error" + }, + { + "inputs" : [], + "name" : "EnforcedPause", + "type" : "error" + }, + { + "inputs" : [], + "name" : "EpochManagerAndBitInvalidatorsAreIncompatible", + "type" : "error" + }, + { + "inputs" : [], + "name" : "EthDepositRejected", + "type" : "error" + }, + { + "inputs" : [], + "name" : "ExpectedPause", + "type" : "error" + }, + { + "inputs" : [], + "name" : "InsufficientBalance", + "type" : "error" + }, + { + "inputs" : [], + "name" : "InvalidMsgValue", + "type" : "error" + }, + { + "inputs" : [], + "name" : "InvalidMsgValue", + "type" : "error" + }, + { + "inputs" : [], + "name" : "InvalidPermit2Transfer", + "type" : "error" + }, + { + "inputs" : [], + "name" : "InvalidShortString", + "type" : "error" + }, + { + "inputs" : [], + "name" : "InvalidatedOrder", + "type" : "error" + }, + { + "inputs" : [], + "name" : "MakingAmountTooLow", + "type" : "error" + }, + { + "inputs" : [], + "name" : "MismatchArraysLengths", + "type" : "error" + }, + { + "inputs" : [], + "name" : "OrderExpired", + "type" : "error" + }, + { + "inputs" : [], + "name" : "OrderIsNotSuitableForMassInvalidation", + "type" : "error" + }, + { + "inputs" : [ + { + "internalType" : "address", + "name" : "owner", + "type" : "address" + } + ], + "name" : "OwnableInvalidOwner", + "type" : "error" + }, + { + "inputs" : [ + { + "internalType" : "address", + "name" : "account", + "type" : "address" + } + ], + "name" : "OwnableUnauthorizedAccount", + "type" : "error" + }, + { + "inputs" : [], + "name" : "PartialFillNotAllowed", + "type" : "error" + }, + { + "inputs" : [], + "name" : "Permit2TransferAmountTooHigh", + "type" : "error" + }, + { + "inputs" : [], + "name" : "PredicateIsNotTrue", + "type" : "error" + }, + { + "inputs" : [], + "name" : "PrivateOrder", + "type" : "error" + }, + { + "inputs" : [], + "name" : "ReentrancyDetected", + "type" : "error" + }, + { + "inputs" : [], + "name" : "RemainingInvalidatedOrder", + "type" : "error" + }, + { + "inputs" : [], + "name" : "ReservesCallFailed", + "type" : "error" + }, + { + "inputs" : [ + { + "internalType" : "uint256", + "name" : "result", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "minReturn", + "type" : "uint256" + } + ], + "name" : "ReturnAmountIsNotEnough", + "type" : "error" + }, + { + "inputs" : [], + "name" : "SafeTransferFailed", + "type" : "error" + }, + { + "inputs" : [], + "name" : "SafeTransferFromFailed", + "type" : "error" + }, + { + "inputs" : [ + { + "internalType" : "bool", + "name" : "success", + "type" : "bool" + }, + { + "internalType" : "bytes", + "name" : "res", + "type" : "bytes" + } + ], + "name" : "SimulationResults", + "type" : "error" + }, + { + "inputs" : [ + { + "internalType" : "string", + "name" : "str", + "type" : "string" + } + ], + "name" : "StringTooLong", + "type" : "error" + }, + { + "inputs" : [], + "name" : "SwapWithZeroAmount", + "type" : "error" + }, + { + "inputs" : [], + "name" : "TakingAmountExceeded", + "type" : "error" + }, + { + "inputs" : [], + "name" : "TakingAmountTooHigh", + "type" : "error" + }, + { + "inputs" : [], + "name" : "TransferFromMakerToTakerFailed", + "type" : "error" + }, + { + "inputs" : [], + "name" : "TransferFromTakerToMakerFailed", + "type" : "error" + }, + { + "inputs" : [], + "name" : "WrongSeriesNonce", + "type" : "error" + }, + { + "inputs" : [], + "name" : "ZeroAddress", + "type" : "error" + }, + { + "inputs" : [], + "name" : "ZeroMinReturn", + "type" : "error" + }, + { + "anonymous" : false, + "inputs" : [ + { + "indexed" : true, + "internalType" : "address", + "name" : "maker", + "type" : "address" + }, + { + "indexed" : false, + "internalType" : "uint256", + "name" : "slotIndex", + "type" : "uint256" + }, + { + "indexed" : false, + "internalType" : "uint256", + "name" : "slotValue", + "type" : "uint256" + } + ], + "name" : "BitInvalidatorUpdated", + "type" : "event" + }, + { + "anonymous" : false, + "inputs" : [], + "name" : "EIP712DomainChanged", + "type" : "event" + }, + { + "anonymous" : false, + "inputs" : [ + { + "indexed" : true, + "internalType" : "address", + "name" : "maker", + "type" : "address" + }, + { + "indexed" : false, + "internalType" : "uint256", + "name" : "series", + "type" : "uint256" + }, + { + "indexed" : false, + "internalType" : "uint256", + "name" : "newEpoch", + "type" : "uint256" + } + ], + "name" : "EpochIncreased", + "type" : "event" + }, + { + "anonymous" : false, + "inputs" : [ + { + "indexed" : false, + "internalType" : "bytes32", + "name" : "orderHash", + "type" : "bytes32" + } + ], + "name" : "OrderCancelled", + "type" : "event" + }, + { + "anonymous" : false, + "inputs" : [ + { + "indexed" : false, + "internalType" : "bytes32", + "name" : "orderHash", + "type" : "bytes32" + }, + { + "indexed" : false, + "internalType" : "uint256", + "name" : "remainingAmount", + "type" : "uint256" + } + ], + "name" : "OrderFilled", + "type" : "event" + }, + { + "anonymous" : false, + "inputs" : [ + { + "indexed" : true, + "internalType" : "address", + "name" : "previousOwner", + "type" : "address" + }, + { + "indexed" : true, + "internalType" : "address", + "name" : "newOwner", + "type" : "address" + } + ], + "name" : "OwnershipTransferred", + "type" : "event" + }, + { + "anonymous" : false, + "inputs" : [ + { + "indexed" : false, + "internalType" : "address", + "name" : "account", + "type" : "address" + } + ], + "name" : "Paused", + "type" : "event" + }, + { + "anonymous" : false, + "inputs" : [ + { + "indexed" : false, + "internalType" : "address", + "name" : "account", + "type" : "address" + } + ], + "name" : "Unpaused", + "type" : "event" + }, + { + "inputs" : [ + { + "internalType" : "uint96", + "name" : "series", + "type" : "uint96" + }, + { + "internalType" : "uint256", + "name" : "amount", + "type" : "uint256" + } + ], + "name" : "advanceEpoch", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "uint256", + "name" : "offsets", + "type" : "uint256" + }, + { + "internalType" : "bytes", + "name" : "data", + "type" : "bytes" + } + ], + "name" : "and", + "outputs" : [ + { + "internalType" : "bool", + "name" : "", + "type" : "bool" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "address", + "name" : "target", + "type" : "address" + }, + { + "internalType" : "bytes", + "name" : "data", + "type" : "bytes" + } + ], + "name" : "arbitraryStaticCall", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "", + "type" : "uint256" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "address", + "name" : "maker", + "type" : "address" + }, + { + "internalType" : "uint256", + "name" : "slot", + "type" : "uint256" + } + ], + "name" : "bitInvalidatorForOrder", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "", + "type" : "uint256" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "MakerTraits", + "name" : "makerTraits", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "additionalMask", + "type" : "uint256" + } + ], + "name" : "bitsInvalidateForOrder", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "MakerTraits", + "name" : "makerTraits", + "type" : "uint256" + }, + { + "internalType" : "bytes32", + "name" : "orderHash", + "type" : "bytes32" + } + ], + "name" : "cancelOrder", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "MakerTraits[]", + "name" : "makerTraits", + "type" : "uint256[]" + }, + { + "internalType" : "bytes32[]", + "name" : "orderHashes", + "type" : "bytes32[]" + } + ], + "name" : "cancelOrders", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "bytes", + "name" : "predicate", + "type" : "bytes" + } + ], + "name" : "checkPredicate", + "outputs" : [ + { + "internalType" : "bool", + "name" : "", + "type" : "bool" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "contract IClipperExchange", + "name" : "clipperExchange", + "type" : "address" + }, + { + "internalType" : "Address", + "name" : "srcToken", + "type" : "uint256" + }, + { + "internalType" : "contract IERC20", + "name" : "dstToken", + "type" : "address" + }, + { + "internalType" : "uint256", + "name" : "inputAmount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "outputAmount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "goodUntil", + "type" : "uint256" + }, + { + "internalType" : "bytes32", + "name" : "r", + "type" : "bytes32" + }, + { + "internalType" : "bytes32", + "name" : "vs", + "type" : "bytes32" + } + ], + "name" : "clipperSwap", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "returnAmount", + "type" : "uint256" + } + ], + "stateMutability" : "payable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "contract IClipperExchange", + "name" : "clipperExchange", + "type" : "address" + }, + { + "internalType" : "address payable", + "name" : "recipient", + "type" : "address" + }, + { + "internalType" : "Address", + "name" : "srcToken", + "type" : "uint256" + }, + { + "internalType" : "contract IERC20", + "name" : "dstToken", + "type" : "address" + }, + { + "internalType" : "uint256", + "name" : "inputAmount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "outputAmount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "goodUntil", + "type" : "uint256" + }, + { + "internalType" : "bytes32", + "name" : "r", + "type" : "bytes32" + }, + { + "internalType" : "bytes32", + "name" : "vs", + "type" : "bytes32" + } + ], + "name" : "clipperSwapTo", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "returnAmount", + "type" : "uint256" + } + ], + "stateMutability" : "payable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "address", + "name" : "", + "type" : "address" + }, + { + "internalType" : "address", + "name" : "", + "type" : "address" + }, + { + "internalType" : "address", + "name" : "inCoin", + "type" : "address" + }, + { + "internalType" : "uint256", + "name" : "dx", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "", + "type" : "uint256" + } + ], + "name" : "curveSwapCallback", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [], + "name" : "eip712Domain", + "outputs" : [ + { + "internalType" : "bytes1", + "name" : "fields", + "type" : "bytes1" + }, + { + "internalType" : "string", + "name" : "name", + "type" : "string" + }, + { + "internalType" : "string", + "name" : "version", + "type" : "string" + }, + { + "internalType" : "uint256", + "name" : "chainId", + "type" : "uint256" + }, + { + "internalType" : "address", + "name" : "verifyingContract", + "type" : "address" + }, + { + "internalType" : "bytes32", + "name" : "salt", + "type" : "bytes32" + }, + { + "internalType" : "uint256[]", + "name" : "extensions", + "type" : "uint256[]" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "address", + "name" : "maker", + "type" : "address" + }, + { + "internalType" : "uint96", + "name" : "series", + "type" : "uint96" + } + ], + "name" : "epoch", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "", + "type" : "uint256" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "address", + "name" : "maker", + "type" : "address" + }, + { + "internalType" : "uint256", + "name" : "series", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "makerEpoch", + "type" : "uint256" + } + ], + "name" : "epochEquals", + "outputs" : [ + { + "internalType" : "bool", + "name" : "", + "type" : "bool" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "uint256", + "name" : "value", + "type" : "uint256" + }, + { + "internalType" : "bytes", + "name" : "data", + "type" : "bytes" + } + ], + "name" : "eq", + "outputs" : [ + { + "internalType" : "bool", + "name" : "", + "type" : "bool" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "uint256", + "name" : "minReturn", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex", + "type" : "uint256" + } + ], + "name" : "ethUnoswap", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "returnAmount", + "type" : "uint256" + } + ], + "stateMutability" : "payable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "uint256", + "name" : "minReturn", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex2", + "type" : "uint256" + } + ], + "name" : "ethUnoswap2", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "returnAmount", + "type" : "uint256" + } + ], + "stateMutability" : "payable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "uint256", + "name" : "minReturn", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex2", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex3", + "type" : "uint256" + } + ], + "name" : "ethUnoswap3", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "returnAmount", + "type" : "uint256" + } + ], + "stateMutability" : "payable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "Address", + "name" : "to", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "minReturn", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex", + "type" : "uint256" + } + ], + "name" : "ethUnoswapTo", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "returnAmount", + "type" : "uint256" + } + ], + "stateMutability" : "payable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "Address", + "name" : "to", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "minReturn", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex2", + "type" : "uint256" + } + ], + "name" : "ethUnoswapTo2", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "returnAmount", + "type" : "uint256" + } + ], + "stateMutability" : "payable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "Address", + "name" : "to", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "minReturn", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex2", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex3", + "type" : "uint256" + } + ], + "name" : "ethUnoswapTo3", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "returnAmount", + "type" : "uint256" + } + ], + "stateMutability" : "payable", + "type" : "function" + }, + { + "inputs" : [ + { + "components" : [ + { + "internalType" : "uint256", + "name" : "salt", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "maker", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "receiver", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "makerAsset", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "takerAsset", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "makingAmount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "takingAmount", + "type" : "uint256" + }, + { + "internalType" : "MakerTraits", + "name" : "makerTraits", + "type" : "uint256" + } + ], + "internalType" : "struct IOrderMixin.Order", + "name" : "order", + "type" : "tuple" + }, + { + "internalType" : "bytes", + "name" : "signature", + "type" : "bytes" + }, + { + "internalType" : "uint256", + "name" : "amount", + "type" : "uint256" + }, + { + "internalType" : "TakerTraits", + "name" : "takerTraits", + "type" : "uint256" + } + ], + "name" : "fillContractOrder", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "", + "type" : "uint256" + }, + { + "internalType" : "bytes32", + "name" : "", + "type" : "bytes32" + } + ], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "components" : [ + { + "internalType" : "uint256", + "name" : "salt", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "maker", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "receiver", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "makerAsset", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "takerAsset", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "makingAmount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "takingAmount", + "type" : "uint256" + }, + { + "internalType" : "MakerTraits", + "name" : "makerTraits", + "type" : "uint256" + } + ], + "internalType" : "struct IOrderMixin.Order", + "name" : "order", + "type" : "tuple" + }, + { + "internalType" : "bytes", + "name" : "signature", + "type" : "bytes" + }, + { + "internalType" : "uint256", + "name" : "amount", + "type" : "uint256" + }, + { + "internalType" : "TakerTraits", + "name" : "takerTraits", + "type" : "uint256" + }, + { + "internalType" : "bytes", + "name" : "args", + "type" : "bytes" + } + ], + "name" : "fillContractOrderArgs", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "", + "type" : "uint256" + }, + { + "internalType" : "bytes32", + "name" : "", + "type" : "bytes32" + } + ], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "components" : [ + { + "internalType" : "uint256", + "name" : "salt", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "maker", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "receiver", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "makerAsset", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "takerAsset", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "makingAmount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "takingAmount", + "type" : "uint256" + }, + { + "internalType" : "MakerTraits", + "name" : "makerTraits", + "type" : "uint256" + } + ], + "internalType" : "struct IOrderMixin.Order", + "name" : "order", + "type" : "tuple" + }, + { + "internalType" : "bytes32", + "name" : "r", + "type" : "bytes32" + }, + { + "internalType" : "bytes32", + "name" : "vs", + "type" : "bytes32" + }, + { + "internalType" : "uint256", + "name" : "amount", + "type" : "uint256" + }, + { + "internalType" : "TakerTraits", + "name" : "takerTraits", + "type" : "uint256" + } + ], + "name" : "fillOrder", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "", + "type" : "uint256" + }, + { + "internalType" : "bytes32", + "name" : "", + "type" : "bytes32" + } + ], + "stateMutability" : "payable", + "type" : "function" + }, + { + "inputs" : [ + { + "components" : [ + { + "internalType" : "uint256", + "name" : "salt", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "maker", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "receiver", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "makerAsset", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "takerAsset", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "makingAmount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "takingAmount", + "type" : "uint256" + }, + { + "internalType" : "MakerTraits", + "name" : "makerTraits", + "type" : "uint256" + } + ], + "internalType" : "struct IOrderMixin.Order", + "name" : "order", + "type" : "tuple" + }, + { + "internalType" : "bytes32", + "name" : "r", + "type" : "bytes32" + }, + { + "internalType" : "bytes32", + "name" : "vs", + "type" : "bytes32" + }, + { + "internalType" : "uint256", + "name" : "amount", + "type" : "uint256" + }, + { + "internalType" : "TakerTraits", + "name" : "takerTraits", + "type" : "uint256" + }, + { + "internalType" : "bytes", + "name" : "args", + "type" : "bytes" + } + ], + "name" : "fillOrderArgs", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "", + "type" : "uint256" + }, + { + "internalType" : "bytes32", + "name" : "", + "type" : "bytes32" + } + ], + "stateMutability" : "payable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "uint256", + "name" : "value", + "type" : "uint256" + }, + { + "internalType" : "bytes", + "name" : "data", + "type" : "bytes" + } + ], + "name" : "gt", + "outputs" : [ + { + "internalType" : "bool", + "name" : "", + "type" : "bool" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [ + { + "components" : [ + { + "internalType" : "uint256", + "name" : "salt", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "maker", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "receiver", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "makerAsset", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "takerAsset", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "makingAmount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "takingAmount", + "type" : "uint256" + }, + { + "internalType" : "MakerTraits", + "name" : "makerTraits", + "type" : "uint256" + } + ], + "internalType" : "struct IOrderMixin.Order", + "name" : "order", + "type" : "tuple" + } + ], + "name" : "hashOrder", + "outputs" : [ + { + "internalType" : "bytes32", + "name" : "", + "type" : "bytes32" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "uint96", + "name" : "series", + "type" : "uint96" + } + ], + "name" : "increaseEpoch", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "uint256", + "name" : "value", + "type" : "uint256" + }, + { + "internalType" : "bytes", + "name" : "data", + "type" : "bytes" + } + ], + "name" : "lt", + "outputs" : [ + { + "internalType" : "bool", + "name" : "", + "type" : "bool" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "bytes", + "name" : "data", + "type" : "bytes" + } + ], + "name" : "not", + "outputs" : [ + { + "internalType" : "bool", + "name" : "", + "type" : "bool" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "uint256", + "name" : "offsets", + "type" : "uint256" + }, + { + "internalType" : "bytes", + "name" : "data", + "type" : "bytes" + } + ], + "name" : "or", + "outputs" : [ + { + "internalType" : "bool", + "name" : "", + "type" : "bool" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [], + "name" : "owner", + "outputs" : [ + { + "internalType" : "address", + "name" : "", + "type" : "address" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [], + "name" : "pause", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [], + "name" : "paused", + "outputs" : [ + { + "internalType" : "bool", + "name" : "", + "type" : "bool" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "bytes", + "name" : "permit", + "type" : "bytes" + }, + { + "internalType" : "bytes", + "name" : "action", + "type" : "bytes" + } + ], + "name" : "permitAndCall", + "outputs" : [], + "stateMutability" : "payable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "address", + "name" : "maker", + "type" : "address" + }, + { + "internalType" : "bytes32", + "name" : "orderHash", + "type" : "bytes32" + } + ], + "name" : "rawRemainingInvalidatorForOrder", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "", + "type" : "uint256" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "address", + "name" : "maker", + "type" : "address" + }, + { + "internalType" : "bytes32", + "name" : "orderHash", + "type" : "bytes32" + } + ], + "name" : "remainingInvalidatorForOrder", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "", + "type" : "uint256" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [], + "name" : "renounceOwnership", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "contract IERC20", + "name" : "token", + "type" : "address" + }, + { + "internalType" : "uint256", + "name" : "amount", + "type" : "uint256" + } + ], + "name" : "rescueFunds", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "address", + "name" : "target", + "type" : "address" + }, + { + "internalType" : "bytes", + "name" : "data", + "type" : "bytes" + } + ], + "name" : "simulate", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "contract IAggregationExecutor", + "name" : "executor", + "type" : "address" + }, + { + "components" : [ + { + "internalType" : "contract IERC20", + "name" : "srcToken", + "type" : "address" + }, + { + "internalType" : "contract IERC20", + "name" : "dstToken", + "type" : "address" + }, + { + "internalType" : "address payable", + "name" : "srcReceiver", + "type" : "address" + }, + { + "internalType" : "address payable", + "name" : "dstReceiver", + "type" : "address" + }, + { + "internalType" : "uint256", + "name" : "amount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "minReturnAmount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "flags", + "type" : "uint256" + } + ], + "internalType" : "struct GenericRouter.SwapDescription", + "name" : "desc", + "type" : "tuple" + }, + { + "internalType" : "bytes", + "name" : "data", + "type" : "bytes" + } + ], + "name" : "swap", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "returnAmount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "spentAmount", + "type" : "uint256" + } + ], + "stateMutability" : "payable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "address", + "name" : "newOwner", + "type" : "address" + } + ], + "name" : "transferOwnership", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "int256", + "name" : "amount0Delta", + "type" : "int256" + }, + { + "internalType" : "int256", + "name" : "amount1Delta", + "type" : "int256" + }, + { + "internalType" : "bytes", + "name" : "", + "type" : "bytes" + } + ], + "name" : "uniswapV3SwapCallback", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "Address", + "name" : "token", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "amount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "minReturn", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex", + "type" : "uint256" + } + ], + "name" : "unoswap", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "returnAmount", + "type" : "uint256" + } + ], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "Address", + "name" : "token", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "amount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "minReturn", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex2", + "type" : "uint256" + } + ], + "name" : "unoswap2", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "returnAmount", + "type" : "uint256" + } + ], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "Address", + "name" : "token", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "amount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "minReturn", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex2", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex3", + "type" : "uint256" + } + ], + "name" : "unoswap3", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "returnAmount", + "type" : "uint256" + } + ], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "Address", + "name" : "to", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "token", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "amount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "minReturn", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex", + "type" : "uint256" + } + ], + "name" : "unoswapTo", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "returnAmount", + "type" : "uint256" + } + ], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "Address", + "name" : "to", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "token", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "amount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "minReturn", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex2", + "type" : "uint256" + } + ], + "name" : "unoswapTo2", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "returnAmount", + "type" : "uint256" + } + ], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "Address", + "name" : "to", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "token", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "amount", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "minReturn", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex2", + "type" : "uint256" + }, + { + "internalType" : "Address", + "name" : "dex3", + "type" : "uint256" + } + ], + "name" : "unoswapTo3", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "returnAmount", + "type" : "uint256" + } + ], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [], + "name" : "unpause", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "stateMutability" : "payable", + "type" : "receive" + } +] diff --git a/tests/ragger/abis/poap.abi.json b/tests/ragger/abis/poap.abi.json new file mode 100644 index 000000000..dba0f0f47 --- /dev/null +++ b/tests/ragger/abis/poap.abi.json @@ -0,0 +1,359 @@ +[ + { + "inputs" : [ + { + "internalType" : "address", + "name" : "_poapContractAddress", + "type" : "address" + }, + { + "internalType" : "address", + "name" : "_validSigner", + "type" : "address" + }, + { + "internalType" : "address payable", + "name" : "_feeReceiver", + "type" : "address" + }, + { + "internalType" : "uint256", + "name" : "_migrationFee", + "type" : "uint256" + } + ], + "stateMutability" : "nonpayable", + "type" : "constructor" + }, + { + "anonymous" : false, + "inputs" : [ + { + "indexed" : true, + "internalType" : "address", + "name" : "previousFeeReceiver", + "type" : "address" + }, + { + "indexed" : true, + "internalType" : "address", + "name" : "newFeeReceiver", + "type" : "address" + } + ], + "name" : "FeeReceiverChange", + "type" : "event" + }, + { + "anonymous" : false, + "inputs" : [ + { + "indexed" : true, + "internalType" : "uint256", + "name" : "previousFeeReceiver", + "type" : "uint256" + }, + { + "indexed" : true, + "internalType" : "uint256", + "name" : "newFeeReceiver", + "type" : "uint256" + } + ], + "name" : "MigrationFeeChange", + "type" : "event" + }, + { + "anonymous" : false, + "inputs" : [ + { + "indexed" : true, + "internalType" : "address", + "name" : "previousOwner", + "type" : "address" + }, + { + "indexed" : true, + "internalType" : "address", + "name" : "newOwner", + "type" : "address" + } + ], + "name" : "OwnershipTransferred", + "type" : "event" + }, + { + "anonymous" : false, + "inputs" : [ + { + "indexed" : false, + "internalType" : "address", + "name" : "account", + "type" : "address" + } + ], + "name" : "Paused", + "type" : "event" + }, + { + "anonymous" : false, + "inputs" : [ + { + "indexed" : false, + "internalType" : "address", + "name" : "account", + "type" : "address" + } + ], + "name" : "Unpaused", + "type" : "event" + }, + { + "anonymous" : false, + "inputs" : [ + { + "indexed" : true, + "internalType" : "address", + "name" : "previousValidSigner", + "type" : "address" + }, + { + "indexed" : true, + "internalType" : "address", + "name" : "newValidSigner", + "type" : "address" + } + ], + "name" : "ValidSignerChange", + "type" : "event" + }, + { + "anonymous" : false, + "inputs" : [ + { + "indexed" : false, + "internalType" : "bytes", + "name" : "_signature", + "type" : "bytes" + } + ], + "name" : "VerifiedSignature", + "type" : "event" + }, + { + "inputs" : [], + "name" : "NAME", + "outputs" : [ + { + "internalType" : "string", + "name" : "", + "type" : "string" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [], + "name" : "feeReceiver", + "outputs" : [ + { + "internalType" : "address payable", + "name" : "", + "type" : "address" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [], + "name" : "migrationFee", + "outputs" : [ + { + "internalType" : "uint256", + "name" : "", + "type" : "uint256" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "uint256", + "name" : "eventId", + "type" : "uint256" + }, + { + "internalType" : "uint256", + "name" : "tokenId", + "type" : "uint256" + }, + { + "internalType" : "address", + "name" : "receiver", + "type" : "address" + }, + { + "internalType" : "uint256", + "name" : "expirationTime", + "type" : "uint256" + }, + { + "internalType" : "bytes", + "name" : "signature", + "type" : "bytes" + } + ], + "name" : "mintToken", + "outputs" : [ + { + "internalType" : "bool", + "name" : "", + "type" : "bool" + } + ], + "stateMutability" : "payable", + "type" : "function" + }, + { + "inputs" : [], + "name" : "owner", + "outputs" : [ + { + "internalType" : "address", + "name" : "", + "type" : "address" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [], + "name" : "pause", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [], + "name" : "paused", + "outputs" : [ + { + "internalType" : "bool", + "name" : "", + "type" : "bool" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "bytes", + "name" : "", + "type" : "bytes" + } + ], + "name" : "processed", + "outputs" : [ + { + "internalType" : "bool", + "name" : "", + "type" : "bool" + } + ], + "stateMutability" : "view", + "type" : "function" + }, + { + "inputs" : [], + "name" : "renounceOwnership", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [], + "name" : "renouncePoapAdmin", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "address payable", + "name" : "_feeReceiver", + "type" : "address" + } + ], + "name" : "setFeeReceiver", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "uint256", + "name" : "_migrationFee", + "type" : "uint256" + } + ], + "name" : "setMigrationFee", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "address", + "name" : "_validSigner", + "type" : "address" + } + ], + "name" : "setValidSigner", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [ + { + "internalType" : "address", + "name" : "newOwner", + "type" : "address" + } + ], + "name" : "transferOwnership", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [], + "name" : "unpause", + "outputs" : [], + "stateMutability" : "nonpayable", + "type" : "function" + }, + { + "inputs" : [], + "name" : "validSigner", + "outputs" : [ + { + "internalType" : "address", + "name" : "", + "type" : "address" + } + ], + "stateMutability" : "view", + "type" : "function" + } +] diff --git a/tests/ragger/snapshots/flex/test_1inch/00000.png b/tests/ragger/snapshots/flex/test_1inch/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..bb8112a2694ee580d3f85642aa509a76fad3a963 GIT binary patch literal 13453 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�j5XKAv0RvZC5iCt#}i6 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 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/flex/test_1inch/00002.png b/tests/ragger/snapshots/flex/test_1inch/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..68b268c9f88e4571d18d6e67d5d4af8ee350d030 GIT binary patch literal 6964 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 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/flex/test_1inch/00003.png b/tests/ragger/snapshots/flex/test_1inch/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..e1a36532aa283ea587033f609b876d4c585fe870 GIT binary patch literal 11536 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 UPsf12D=dzNgdJ`>c;@PV1L0-a-2eap literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/flex/test_1inch/00005.png b/tests/ragger/snapshots/flex/test_1inch/00005.png new file mode 100644 index 0000000000000000000000000000000000000000..dabe7afea35d5f2c73b04e4e20993035bd748cb3 GIT binary patch literal 12445 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 Tod^HA2AEtkzglMa_~riq3kDy; literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/flex/test_nft/00004.png b/tests/ragger/snapshots/flex/test_nft/00004.png new file mode 100644 index 0000000000000000000000000000000000000000..3ade03afd0abc0a54dfba1b4568c55c88cb14912 GIT binary patch literal 10360 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 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/flex/test_nft/00005.png b/tests/ragger/snapshots/flex/test_nft/00005.png new file mode 100644 index 0000000000000000000000000000000000000000..b2c5fa98cdcb46c701eefd94873acb56be853447 GIT binary patch literal 12650 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 UPsf12D=dzNgdJ`>c;@PV1L0-a-2eap literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/flex/test_nft/00007.png b/tests/ragger/snapshots/flex/test_nft/00007.png new file mode 100644 index 0000000000000000000000000000000000000000..dabe7afea35d5f2c73b04e4e20993035bd748cb3 GIT binary patch literal 12445 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 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/flex/test_poap/00002.png b/tests/ragger/snapshots/flex/test_poap/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..aa24355a906dec2fa5b120bce49d0ac4c19d65af GIT binary patch literal 10206 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 T59vX&hNrHgtz4t{^yU8o#k81{ literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/flex/test_poap/00004.png b/tests/ragger/snapshots/flex/test_poap/00004.png new file mode 100644 index 0000000000000000000000000000000000000000..1913ce7c46ad82c8fe6aa2b334f486e30163cc43 GIT binary patch literal 11569 zcmeHt=UY=xw>C|r7ezpt*iiwcL#T>?bP+=p5Ru+Q2oOkUDkxGElukg9VnVOd0)m1V zAhgf|M2Qe2gcf=TobB&<&bi*}djEj)d^q`%*?Z5PnZ0JMHTSyLdUe;#fSW^vgMop8 z+wjiqdkhSWKN%R9Y}lB9J4!tT1`G_+yN0)K-Ve>8%uwnJ(Cx7S3uTea2Syzys#v5M zjiXM~0e?n=C-PaY+%$S~VjKEa$@a9wxpSRD+gngMOu)5E@r`HMe_^-6xVr`!8Q7?i z#zG7XgNaeB3?JA$82IAxR`3$ zeo6}DtOfzyh*ZvE0lhEhjBNOsL{M_QCR_tDU*my!;sSt00dMO8Z+E?^cojV}0$j-e zR~?vFSTOk0;e2z2Q|$Hh2Vd+C{Qod9T%YJ+#l3ih^BsqPLF9yBhw&Oy!cK{-^o@&5 zBEyj4K!4qI!@8Vq4gXR|~^{jp#?r@TRp8igdSkE{ak~PO< zS$K^5!+q`8EX4YT!21Bh@-SnYp)-&82l=jINyh?7#^=@_;1RVtR;gFT6ru#?*BEx+`$({PQ;nQL!F3`9>iQ0xz;4B`f#E zN5;vxQM% z{PTQd#W>0@g+U$%nfaWSPlBRXvzy99zC#=?8$Oz?%$i8ldZTE1TJ$+ADB$6-2jgJ4 z_`U>g#v-lA?-J@t&_eBxn$`!hl%mxFsdKs29Ci8CBDQG|N0qT1`nJ~eBKy#Tao604 zvSaW;n?$sskgKhmZ86aXq7ot-J?iJmoHFz+T8TX-Uje!$mJHZN)3A|nY#*vCelVsx zOOcgTbQH`%JN(fKD&t{He9zu>7uzhF1clg~XA&VRJhD+YJONUv0xV&y2jUReUzna@ z9rxF)jBfIAEiXQCNUv}&6JSiN7pWQLr!G}_0VcETYH*y4bH)LtkO3caAf1lw_-inY zXxoqqa@Ms?X7AfNE?c1N-i34Yj=xr^SwEHw<#oVLuri6*KMW2EMf9|Se3%&%6YrM0 zL7F;?Fia-TaLZNVOUtK##heBlQI{5($40zal|e^}Nrd6~xqQa|ZE<7UcJTy;TRe|Yq*5wmb2WrYzD}hmE(=qcY zB&gHN$|*{)r3#F&qv1c+zr7V^Ix@Pf_tZh?LG51s^Mijx?m?bl% z@!=i#)`bJ*ZuWzlNJ`M9U4r&Ndz<8_C^KLoGt?ab4&B1H+UMzd&S%0mt=+guF0b=bC79^T=X{|h<6nZ0=P-Vt3N*yw49{`86E69qW04d`TR##xr|S3nPuxWnUN!Na00--kl6S73 z2)}EjlY0sy@_OJLzFxKa?Ox_1r^ZDqkzVZt!fFU6qGlPP?5wvurz(rN2azrnyU}yB zmHqR8GKNATr8uaHE7U!WxzwlnZC#d>%O^7eC4yRF5u}TT!_b4_-i>Te<;2{IE_|>( zb~q-u*^WDLHkLmwPuI<=;q{P8&j{=>Df7M?Z+Od@ zi%!Hy(0PnAj%)6`x(bggS}LKrI!m8?3A9WEGdB{(EB^ z&J6Bq1+Of*y;KQPi#@*iIb>IbbrXx?XWw$%EJ~R_FW2a)R{nkYel<(o{Iw5fMkedM zcKj3*cHNX08;cyKuw-x6&EF+YZH@dQJY^~Yvk;DT7B&+dT#$Xdx2H(RxnQ`jjQ;u0 z*E&i(q#xk%_&tU|;Uwv_s9|N7?vC?SyRidM@W+54+}u zNPtx+mafj3;jTh*|kDg&|j<_opmZ$pU z+&1w-PKts38w1jxOHA$gThf_3;m3Ai$GFZC<^u%n{YC-*QGW$fvx|X6WmT&_PKlzf z(!7suH#S}RKvYe#9IwyfEQ5!Cn!drcbq4^B0AQelma6lk@le$71$WIOb~JHYF`97H zfB!LF(2;j4K#TxD66-=n+q6t#la~PGJ3D9BP6I`T3qKZ}B<&rEB@80!>?@d;Tn>Pad9c zFXH^2gLS7KH|f7*WqE0|YD+^w1@K6^ico}s#pp_~RD8kPp<6~;o%Y&m3!lG-#4F6Y zU9It@LIN^+cWch8vN6FwYWyxa3}LOy4Dih2acZ429_0{T3bsunQEij2mCCWw$IXpq zZJz!bxz+Bui`ce0(|Mq5`PMhP@>rWnD{Sp$g>3DRFLeL80(Y9L)$bsX3}S#Ay^3P3 zM;qj|Y&rKjjN{6+!*#L?llhCSX#Kn`!7Vc@PgK7KOsA*hD#&VGZ-TNuAz+``e$;Q=jmwMKTOv`LYB?5aZfw8W zbCUmwx!sx)>|?d0inLU>suADo_{twcNzn#C_2ZPwS))F z=9GjrrYkqpR<4dTpj!-oM>%%dhX4B?9_XQb5N^76H8L|ozOX}YxWN|uIy!ChU zXsOE^Z=^?uMG9dW@2^o)u2Im}Ys04;>#^D|g2hE$tw7jB!I~1dBY(?T+$is&z2&!Q zaChz!y;oghP4fU@TQa$aK6-kVN5*snU9{7qY1}{I0FwwJokJ1B;!`hp=FCG{W=8Av zHof8XXs-){tp;00D*IxiVTKEusqcU@Q>FPfx!JGIZ@>5@?C{EO=-<5JW2JE$4N0)A4;%gRX_ZFs`-S`<1AItLcBbDtTr1pA$sxC$lOX*x z|6xPxXo=dIZC?6=%7tI_t+7Du1A$G%a;sga#}#I>R6DRaYk1If3S%2vplr_^BR7NK z3G*$9@(MC$z6MW`&b_^$s#Lqec&FjOE7(#lyF0Gy1NGiWqM<{3;52iGozLa`{@26E zBsV1jru9wfWQ~NJ9t*=l-6t&%NK)4u!yB7g%y0ZH;e!?ADR(Qi$m=Plpuaa{JS;5) zyfp4AmEuOPRbx7sb@0La`Jy0mRo19ellO&=Gx)Fri@C)=$RH{3x8WQ7hHs<>E4%vO zB~-FrC)v^Cet+ZMlT1W+b0Krdiiu+n5NtW>^4E22&dd2G|DP2@dmHp}07aCKXk@)K- z3clh_jzUHbW270QUm@r6*Qe}%71_uy&XA{C@k>69j+)r)lBC>m2JbuxrH>jNyo`xw z`_}Fy_QVcQEojQPJ0wZAig7Y=JrqE!Br2dwvJcn&^m(~P{6~kl=5BEHSG*^0c^yK& zzGaL!%Op~%)QMVkUN{ELMf^(C$Huzuc$a;We`26SQ|Z(3af%Av0A=-)29!52#~HX; z6dgr{9kq|PI~vYRE{YFOrw^Om^jj*_Kb9_R@+z1(vCfeoib7QsZ^_8e4}J&Aeb2hz z^_ekf2AZ`zklha!(7WEl-tQeg4)Jy5E=i8CAnf#K`hvmr7zHyYaokqYdo)t1!d1%< zTIE`ozdpP+HgAsNMy;ygYnijVw2zdnpGj6}Pdw1G>Yu(5eya1vL9n<}!Lh_fAkh1| zKfob}Z|^dnWb3-CQt-OqhPk$*t z;%#85n}jJ7v0NThxkB5e2~uzia4G?a&7KZ5tb(#A77O%Lqh~xWsdSIT`j5b>TpyNK za=LCf@)(P&izge@QkJ2PG?-xiNyfz994sk7ygyhstJKwYX<3=3GIepUil6Q$4uHG8 z(E5(=i!X}S@KssjvzBS|doaIgTQImVKqU58&g({{TUG1hQt_Ky?a~;>?ISbrUv<=S zMU5YQ6_V@cx!%@drbkj#s-4^&LuD&a_6PPN)ZqOv9nxHI>S*@olgR2iJaYQNb4j+K6hS>XjoQAbmDb@O zErAN737!}|d;Rh1^{e~Jg)zc$QQane-TrB=hACXHWNOtP-JwGB@s#af;Ce@ZdT1Ou z?wHHq@o1UpdRx8Oix+C}N8e9i6TcOzTem9&jz!E10OXh@jz5F%+4GF#(}T8YZDM8W z63NY%RoG&J)$0Q8<-regpfXcPWHE>$x+}pXLS2Uw`Ua_cK8W}30KNA_HRJS@TG`GD zR4&o!!pGyd4Sh*Wxla7WTfa8)b!nZwk9T^0wif4i~& z@n`?n_PS-&^}@K7(>+f;lUnt zLktEens@VH%NB@-j4<*A)LFq9GhzC z0}N~Z{Z$kHa{)vY>kZAbj63Y?V99(&6O&tYrjOPEvW}s+F0|CtdvXbAUaGAQvJwPH zzJF;)adq!PZOwgv-D5a8@dR}3_5C;hPT%BB!Kk*G?ODJlv+3zD?|#4TsdY`kI_=+& ziFD*bt-SktU?D)pr`zC1&4XsP4!8ag@G8KyaF0VA#^K~Ef4Q=qwsY{Bb^fiX*cxfx zI9-SF3i1MUg?2c6v2!}Y`6zaSgw4yWV2WsSj6|>Q!IrEy;YY^iP~)uPFq&sp68<}J zeNdFW>&es!4);qNDq51?cjlj*@I{1#q6HN}Gkz+Nt{u9+*hL1L>r7#W0Hcs>08wX) z(ZHt5VC_>bs7WLvU!ONjDSG{^i9U76?TT;&*zohmZmxxD{HOuuvWB3UYtQD(A;`@) z`J^2<^0WPZRWw#(71bF;A8%`yg=hqxmsu>3@@upQgT00UXLghCxwC3-mA%8bZQ7q9 z4J;K#(9Si>^HE50ArYX@^(;!&#d|DXtitB3y{BeBuhN`_gUvoz@>(?(N-br(mCUvk zNcAz1L(HuwsTx!n8O+Ahrc%6T7E)FKQj!Iw(9lTLwrd{t{^wN6;aLEa*!b9zj|Jcm zsIQnyCbzgF0n824GBB^Ljg-2{XJRvQc0;4lzm@*(tp7to3&2fYEk|FDFF+>(;R!%J zTDJ~aURYCc4KRS8ZfSs^&vs%jFs*+Srt3k6RZlj^Xq822vB*&k@A9ILDF zrl#v}X)h)M9p5K_!QTRWlzk|&>BbfUiSj5sAWVtm=v9QQu5hfE$?&p6fY{|EmwnJr zAK6@S_kIVl1v!5rgRSPwAp?znpSq{mzD8Mj4Nzxyp7Xn($bu{{>t}o{4ql)=0p%`N z&hC8e7WEiI)!w0;CZx&o^emnj0(iGQ1`xl*%{z3WZZPLu2Utz$Xg!uN(p?8uTIRtWbkzwAr66(>(#+)wedp2B`x%;r-0 z;6FY(?Izaaui2Q~WRgEWBO03Wdfs_Iy+gk96~wa90K7G&~x_eRyK1JMZ|+3_evUzzh>ea6Tky}a)b&`Fn9^Jj&4rKF!D&VHRtyf zo{%`6b0{$T1aE8Wt&>e^&Bc-i+_IX)p2KhXWl#F}NMHN#E#ScUOx5BEh4ZYBF9a;f_X-Q29NGK`C*wmSM%C>bcD0mFu?dH_~VHT*1Efr+dr2eAO z{jsz6MxSc9O(9=(?6(8f<70rT#T}Pe#sB$k>1qXMO5~Ym_n!I*MNEnZsEUbbs{u6F zI_L>gIPvO>|BvYC!`ZB*X&>1&tJd1AJ~GrkLR>4$cvT2N>`z0to1j0Qy0l#=3P^5R z#J;p_i#*gEvGcB!-83|%h@g1XBJn>d)^$0FGfh@+HTyNO3f3bLAw(m>Ewha1`7ZiJ zx6st_sTivb4gcSF!tXDIwFOr&{%-c89d`eMg(phXMrU5=C6PLdJg1U6gSERaE7e<` zk4}_nRDW40C4wF+O)2&yoHq1YP@?8rQ_|fxHE>QLI_gQO|BwM1Hda?2o+cWlTrjo7 z)ZM`SP|r0>l(KLqz(aA@O#9#6a4M+9&{DIeKB1~Nl|r9M{`GOiwFP6>!Kd!0Tt_Y_sO7aqJWYz9Tn}%{OW<17l=z2aZG9n;*oA z6x?q#s2i;QOc>^$c0~wo##fdOKXeNA`#wV9&2YYr$%*uhoY>)@w&mq1r{&wXzY01rMED5W_)b!Q#`PlN>MhME&I~|kMN-BNa-|!WTLICI=w<)in@i~}f zb7$~)V&xMczujFo2X#b{K2oRXNcJHB3J5BqE2KD-BuYg_y$bFK$8vG`b+3Bf#)=JY z-XxMi=3+f*2Puffz2sAia}56!hn$>%nQxvubpb#pE?uYF{=pr83r22ankp9v{Y51Y zjYKhvt|(m{=8Oq9`LKV%m&VXKxY?~!07!83 z#HpA6Mqtwy6pLOM)ZWrzw&}hn*B0+#1wRj`QOLfqeo&rO*4MRLd*<eaH-B|Bs28c4guVYyk8a&gOBz5g>MkCl27of^Jq-A(8)pE@Pp`d`u#Ep`L&= z*CZmqBGgEy%kl^UB>og5fIIc{PY#;8+Kxfmg09>+oeP{=7YyfUhc)y3K>DXfx{=Pj z&|ERgX!UmJ$iA83*|cBd0L@{13&3ryhK@xQWO_uURxOP_paNwU`2z#xrpcI>G*?jQ z#G8DdD`;&@?_4uoGyT$c(MKDJcCCGdpFR&#JmASDWVS`(y2l=h+zjxM)Ky0dfqkK^ zr%ewk$9Yv=x|W~I74NwcUr@E;INPSYswVzaW8!x#=V3aS6v2mClDma3XHkY1$nq}D1T%(UcZk*8yt0S zzGoCf8N2s*B$|CTRk~PM;f)iTZd4#!TPIuZ@Ol=)3j1F3A%;-A9wfryT(H3xN-)5i zCHCr~S4%vnCV;J*0=V_ytYQEL;*+cd^up)LS80YRi}Kmmk$FWcw#Ml1)!jje91*lP zj9&u_IBGW&po%Dodkj*3M8n7STy)>;Cfx*>LE`!ZV#+pcKBB#mS2yyrIAqC=WXTiv zY=GaywdhY7FXFybDRo-kTpvB-7uK1R^&EMt35f*GMdtc#0o=g(*3&heWfa?#~etxF9gsD%I&VGRbSxh`D+HgG+(b^5%4Pl*(VLQJ952HdG%8NBl~0N}s) zSiCayd`-0-xm)`*QXk{2vIdd@`m6=?T$;0ICt3ZBjunk~UAG29wDQDznjXk{W-TS6 zW~7sd%@uKd@I(E~`8ShEv$r@wNiJ}F4*Wx~QBMGj008!q5g`9K^5=2vr-+1aFDZ+! z@D*e&30|CYO7SN({=3KGv%dQBygT8<%OAfcH1u7Ii-rew&R z%KZj$Fn_wJ?3}5`Kau2U%Q@xK2THO`a|IN7XF5m1y&`US#nL$Cw(BCsF^L~9_j)t~ zNas2V$1WLn&UvNUKtR_j$Vg=$;n*<$A!Z3?Y$lRyM}g!Xcf1;QNh38pMV|9Qx%gkT zE%S8@JW;21rjRa}oj6h>E#r#=#_MX98(vg3%PQW#aWc<792Dm~q4wu62=phQ6*Iq4 zAAh2V%c8n z^}ufvl`xUyhb*mS!RN`DE_;@17UEug%8e93qe^9L?oEi9p0@lEX?oAi^J7tMZMQ5> zs@fzKC`yvC5-(oJuK2X4uj@7p43SBsJX=vs3Tc_~YxK_1d1; zmx$nV8*o~JMNNfY{?>e1ad)RMQdE=F&Ua3`^fm4R)gQrGs!2rE+0~r|DW?)( zKhm#aYx;7`+z}na;}Y)sGpg76J>bPoB*T)WfoRPSUIvuu$W1#^Y7Sk|Ir9ZTSPZZ- zfw**YV-u;SkaU2l19EDi8bCq}s4m1)g08uIuU-T8qzxC><=jpH{1xDFg&ypUZ4a)GL&eEC57YKJF&7x%7$b6?>3Hv|t@k6(|*BR;j;G zcW5b~2gGDEUUxIq4;};3c~!8u9$J@oBK8;H{LgsCI)}D7_RDs@-=3H8gHSx`g&+u4 zNSh-eK0%VLFVz|XLJ4*~SD;*yrp7!ksU@lc^<(4%YhK&uc0H4SoZM}<9+u)M^ZIfA zCDbcnnv3bYj^j|-M!f05VPMmP&5W<{X7Zz`#7GH2e+uLbdhot0BK06BxMAp<+#rUx zBd0*|cS2dCW7ux3)mzL)d39x1W{}qu*HD?0N33<`*wx|5NqwQ73qNWUE5&8EnY*5# z$m9-mvQ=6dg(s_3^C7gJ#+If)l>&cp&1PI%x+iQdKmLxSW(k8 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/flex/test_poap/00005.png b/tests/ragger/snapshots/flex/test_poap/00005.png new file mode 100644 index 0000000000000000000000000000000000000000..be51a9d5595cf5ea25f8f90b684306d30395f329 GIT binary patch literal 6357 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 UPsf12D=dzNgdJ`>c;@PV1L0-a-2eap literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/flex/test_poap/00006.png b/tests/ragger/snapshots/flex/test_poap/00006.png new file mode 100644 index 0000000000000000000000000000000000000000..dabe7afea35d5f2c73b04e4e20993035bd748cb3 GIT binary patch literal 12445 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 UkeP1^=l}o!07*qoM6N<$f;8OZ$p8QV literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_1inch/00001.png b/tests/ragger/snapshots/nanosp/test_1inch/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..a9deb912bea305dba41ca7a504ec72e5b5fd1fac GIT binary patch literal 530 zcmV+t0`2{YP)UH}U_x4 z@tpJEO78PDyMWW|sqY_pEZ?&WxQHF>5!Fv|J0N{39&z#62;2d2MVAEb0A|2s;)*T_ z+yTsh$;1_167MURQ1hplEsbin>Y(|ynmt+~vW1+pj4Rc)NQtWxWKu`&h7uLQ7aMAr zWBZ_lmLf_;QmxON$l2C>#g7+oA1U>p7gWYbt29LpH9Vyd)u27r6>enM1R_S4y;MP%-# z@Qax>O#uJ^0A5Vr{|6j-j-PUq*TY~4$uP%sx;4AUxEXN7u#{J+h%z#(zNWPns%C@# zyk;||o^xgBdqK(xDW7xBj$HTUh+ zo5XuF&Zi9LC>!jvI-slB`H+l?XLUf@|7Ggz<<%X&dM|V{BLDyZ00000006|`2lQHr U3RG4xf&c&j07*qoM6N<$f`gm$A^-pY literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_1inch/00002.png b/tests/ragger/snapshots/nanosp/test_1inch/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..82fbba418b8fbddceca090ba5e95952ec29af2bc GIT binary patch literal 756 zcmV>^4yMNU5myZwk?q&yM@`i{pmgUgcE21*nS`@;k8-(Vorfl`h+w0FTyXLATD(QCiD zFvnJFE5vQ*ly^WiM1;a0S+Z3bKBI~d3xI10A%qY@$TFO4sC#G=$48yXsp_fbWP~19 zOjO#cs@m+f?r23qDJbq9d8?U#mWAQ%5N(KMn)}wEQ zAdlpNkgca>kPwk8r#Q8hh!*;*1JkO;#@6S6>WFpR%n{rjaWy5WD`#0;sLEwX^v zr5vsqPOFJA+re;VHdN;ebK&xhanClwqL3+dYDuA|>xCgkg@i9X5xXNB*B>Jx2pO~n z%nm*Z;!8adJuh&LBfDY50eakLCK?BL=);3S-!W7xO~nr!F5KheSSUC2h(QLm$=uVb z-JN^Hv`@wX-Z6v_LI@%Mhi$)<$7x+FCxSyDE7VdQEwt^}{(1z&X%}cQ_~nmE@0Z#@DyK3jZ3&hr_B^?@QS3Ydr@Y mLtDX}$528DA%qY@Ci4qhUq*mMYg}mn0000RGtYLd`~u(OfzMi6;`-8sQ>Wg1Y{hsp;@ZP?{qIzd z{X168IqBA?-&t=azm}Zvvj4Y}WYJuP*sN_cyk6#qEzFq+w5mb-+uaLE`R?-Be^zV10`oMy}N@eVNRgQC657unyd!WQPx78w= zdEZ{|jj6f&*zPQ4`Poyq!;|TboBbQ_2`4AVZql4m&-W#6^QQMEH#^M^{hq#hj(NMI z+ONwZPQG8%mD#7QaGECNsK)Lr7zcC*GDw(Vp7-QneSuA}F-X|c)z4*}Q$iB}&SsbS literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_1inch/00004.png b/tests/ragger/snapshots/nanosp/test_1inch/00004.png new file mode 100644 index 0000000000000000000000000000000000000000..3a6db3d755f9465e9e9ccddb16507259c1982936 GIT binary patch literal 450 zcmV;z0X_bSP)F4rBsp+LBtRM004mPe$G~(Qo6Qdj1OM( zsjGdsl+r7&YvbGeo&EtuX6Mj_rIHw<=nDDnik>&-_h8)>S6F#od7;Q!#~R+V*6Ekd z+RQ}{FmKiH9*vI4Jgl*<-m}(m(EzXEBBdlogM7D=*Vq{1<}VH<+Dffze=CmYwZxUt-sHtpMPN3&&4xf@+Nv}tUE$Z zLkIp$ec7_qRePtQT{FVl(Wx?B?ya;duA6wBd?fm8#gIvV8zXAc-;6dmiBagJn~-UP srHB^*000000000000000003C#AHp|fw(Q@T-~a#s07*qoM6N<$f{;bh!TfV!9&H?f*TwQL!6n1ey{6006Mu&-zDh+g1*8&ZEeb zW^dsc{Q>4E*}qrni^y+4N-5_o{_HPEzel(61v27BP`zxK$KvOC0Kj*eyccBew6sC7MC#{%#?rJu zWnb)xd)mCgmfvWJWY6o>X}BXo5eZyolY9~^H;*oA4aAR zrM~@%*+0+x4siZ^>NeRI`ky7J)@xC?NAWt?CvZmjVCJo zDK%m@u6_njo5%BW;523!CYkInqokVbFGe3kq6$UgJsgSj0ssI2000000000000000 ar+EQ&UTi;1epT%N0000H)9Sou?uP9KCFrKKF+B( z;dDNobK&3DycRuuINRTY)L*Pr0$5)Gc;%0-)0rFVUG7c?ti&voc!}a z^m_?^%x(_-t=0+IU5P!nJei>HemJV%O^AWi`bi7^_y_{Tz^qV00000 f0000006^Cl!j~X&&{~Bn00000NkvXXu0mjfCg#d| literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_1inch/00007.png b/tests/ragger/snapshots/nanosp/test_1inch/00007.png new file mode 100644 index 0000000000000000000000000000000000000000..a58590b988714545e7960f7f400f360ffc5de41f GIT binary patch literal 382 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|@9hba4!+nDh2#bl+hG9*4k? zQuR0Wo;SViCn%lTo!KM1sAjk5Y(@*V#~rbuHu`7A_H&AxgwHf8y4cdKvg$JbB4n%ez%sZ31aBYd&Oo@(7)_WJ(Znl2o8^PD` zFE`_Y-IS+Kdq0Q2-7r_C=}F(5ev`U`H~)QH|9$1FHE$YjoVax+e0xei%S`SAGlOm! zp7{T3NA-mVOYb&rdU8MdyIHo@@7cE*uj@I+Zu{2i)KMSV-E~omBiSJU000000002Mr|@;RfO%q! zseByAjY?K24=|Ixp?!0QTt+h|R5 zkJCT+T>(X3d+hoXf2W|pG4vt)k3b9n0002sa>fG2m~o+-C-Da511^e0<0W!>ejT~9 zx073Hk#LMLMVLkTEF8y?Ur%Nh<8I@rN?i`G&{qtGi-$A1O^%{m)Rxy(w24uA(Qh*1 z3fNMpKf8WK16`q|mZcH>Vpq0q`6FtrO~vPb7B=oDLu*{0>^D-({muZ?t2hj1zV@cc zL_IB=3Kl0|BAnwHnqR|Y<&yah_3lA>Ob3A0b&#OR}hAbbIw|I6-%G$ zH&0Pj?QTwaYG?Y70{{R3dOnSLS<-40w`iufus3d+ zYpuw=+w)7)j0Ixd1Yw?E6O)3i@T91?m;|k5*p;lr=hp^b#tffgHkVB_x=?(HW{L}= zao4GZ;Z%gK;0orFpeyZ&i$Gjf2Ovn>!gl78prXN|28+$m7UH}U_x4 z@tpJEO78PDyMWW|sqY_pEZ?&WxQHF>5!Fv|J0N{39&z#62;2d2MVAEb0A|2s;)*T_ z+yTsh$;1_167MURQ1hplEsbin>Y(|ynmt+~vW1+pj4Rc)NQtWxWKu`&h7uLQ7aMAr zWBZ_lmLf_;QmxON$l2C>#g7+oA1U>p7gWYbt29LpH9Vyd)u27r6>enM1R_S4y;MP%-# z@Qax>O#uJ^0A5Vr{|6j-j-PUq*TY~4$uP%sx;4AUxEXN7u#{J+h%z#(zNWPns%C@# zyk;||o^xgBdqK(xDW7xBj$HTUh+ zo5XuF&Zi9LC>!jvI-slB`H+l?XLUf@|7Ggz<<%X&dM|V{BLDyZ00000006|`2lQHr U3RG4xf&c&j07*qoM6N<$f`gm$A^-pY literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_nft/00002.png b/tests/ragger/snapshots/nanosp/test_nft/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..1ac2ab07753bb344d9944b4c3935fcd2921d373e GIT binary patch literal 702 zcmV;v0zv(WP)c&zur@%X+Ij*PoU?OAnNg* zN4T4^dIr3nrv^aEjK}fR0F?i==%L;R00000_*iT;)ZFLw$(CoqpczYBSToEP$uRcJ ztaMiHE_xSX&a#);$Ly(hQ(B}KtV|@?tlvwhbEum>Ls$#)OI^xB&XYWO9cJ{+bD_LC zP>iEaKgW!&I4QeF+ZMfwbQf$;&H2Lt*tX4i`oC6%pQ!0Ot9jpY87oIi(s6^i{4cLy zA-WUMlU`vlZlCnX1CXT60J`)%1xHF!x`*ovo98o=HPtlC(^7+T&A(x6M5(A$S7q|GGnDg)R$w? zwIEtIsEPZraFNGyBuhLxWl|Pp@suMi08S~*k7;o3XjA5*OUBbbB)yPT6Im6+DWkG)l^4=fqgnb#dhcJ4Q+}vD;(X|w zv-^1e9@x@hHadr!g=N7Kw93CK@Zb6UJ{rm@0gxW}rnO@3gSwY9e;cZ3*UdK6?CIHD ke)={P000000Dw};FJ0{81JjCfLI3~&07*qoM6N<$f~{Cmb^rhX literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_nft/00003.png b/tests/ragger/snapshots/nanosp/test_nft/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..330755f9fe2708a83b300dcf1079d27ce076d363 GIT binary patch literal 344 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|^K@ba4!+nDh2#AYZcrPebCi zH){9(d+a?e=fk7DF6+v)Q~zx(o4MZVFnTirHQm{}$hx4XV}+rMENfF$G&$5Sf9N(ztI1C+7f>U>5nm%9`hUCTvA`4`)lX2#xI;2`>)!Ee*v2F zLBM_b!H1`xsjp$~3zTdgI z@0z3gedeBz^Lc#3P2D-Y9d?%{hc_7C{%d=E+6I;{G9vy~j7pD$rA)i8eSVn5-t&Lo zEbr;xj?G=X&CmSM>tOAx!Bu(}%70kLZFk_A*RaVd<++B((a$^ocAP(W;?e30C(N3B j`0`vUKwd-w4Yz9;d2ILh)?KSF1qpk)`njxgN@xNA?RJ!r literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_nft/00004.png b/tests/ragger/snapshots/nanosp/test_nft/00004.png new file mode 100644 index 0000000000000000000000000000000000000000..5a3be0cc448c1f4e40f7d3e5b3771ef9c8f6a41d GIT binary patch literal 557 zcmV+|0@D47P)Nkl|GXmri#b>@rI(8l3)f|723 zi_@*VWOliBHX0e^Rd<79FJif?P*UiZ-4uhL4onVDTFAXca>y1ab1i&HHWs@@mXrOJ zmfY|zJ|~S-uttioxGp~hlLHky$>9~AjGpgn5@6rEoO61e(LbS#hAYk}7L2aeafTZD z#l$P1;%FsjnX=zT{Vl~oADElD_9Lub$09qHC8fWY=Fgr7wJ#$ZlM%rp0Z9^J85AiJ zkYipBKr*!oK|~?Q3=X{zJ*UHu#G=t&)zDpo94f3@#HG=y>I^kB z`K@L-#bk%#aQlRn%P4HW_7V3Rv1^bO>SMb#>22=SNR$o#FWJ&U=M`YoAQg5v1%#_1 zw^FZ8t_~?dErQ9MIuI?6FyfPI(T}R3kFxeW-0kysg3c=-c?NwdQc(vr3Z)gDm8zKh vnayQBH2?qr000000000000000z@EGTXu{zxie%){00000NkvXXu0mjfcUbsN literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_nft/00005.png b/tests/ragger/snapshots/nanosp/test_nft/00005.png new file mode 100644 index 0000000000000000000000000000000000000000..d32af33c70806464c001b6ebd09bffa085dfbc43 GIT binary patch literal 562 zcmV-20?qx2P)V~n90}{|ooSSWPa7F@}%ZO!(tmW)IK<9`}7ytkO;5Qkgh9=jXQD?qH4ec~<6O?rS zTbyp?C3DENv(w3>sD?Y7dl4&Lg|b4w?4~&U)G%wF^-y|5)?^EmxfXvU8>`(C&)NP; zOUZml%vmQ5tdSxtq03*vq@m&`Yu*sa?D@VX0rtJiIj7ed{TIr_TnR?;U<|d5Gt|&8 zCS3syM=LqYl>Ij8Zz&GCVQJ>tkFa_jOZ-@tl>RI&o;?q0Uq*3EW(*by$dVbBNh3u9 zN<5bXkUUz2AfYnI($FOdUuT$5!>{C)Cg|Pm>9|16?a1dj<^^sj7M!WzmMt zN>z*-gHKE4`KSc|000000000000000005Tr2CHofjBQ|KeEG@b*Gu*}E^XJ`xA&8bs3!TCl2FN)Z)LPDxuvR(406jEAKCJ3*e!V*&!#MqvlBnOD zv$$Y5a)1HMl(q?LgEI_Zb|ac8;>+1<0M8MNFaQ7m0Kj)LCmTw(IkRFuk_{cKZc8Zf z@ZKa3?o6=RU2ny zLthM?0fx}39%amWOUi4U17EQ>a`hoB@5hK7&6MKX;`$RaP_=hGi-8~%m~ zu?GhOJZJo#i`6ZL)&Aw~g2We{%vzsC|L(E?00000007{_KhS;!EDxWdO8@`>07*qo IM6N<$f}(yGSpWb4 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_nft/00007.png b/tests/ragger/snapshots/nanosp/test_nft/00007.png new file mode 100644 index 0000000000000000000000000000000000000000..f79d5f8db9d17edd0e4dca2637112986789e3ddd GIT binary patch literal 651 zcmV;60(AX}P)MUvl5Jb%j0UJ-dE~bZ{l)L<3h01AKZKyh zH^!(=7z;U|0o1Z31wyuDg!(itLozn zHT1>gJ;1TFl1~|!Z?pWC!2uiAo?QD97LQ|@A4`|QWuf})xlnr<)tHP378{^QAuNL; z#RjM`uMLP~>L^4qlpwPWh9bke@@{CO*jaP9g+jSM-POt+Bv=Q_dO)@%QB_Hql74Lz zHGSIZ+1S-ehfdkO9icVj?qJ%ts3zV|T(= z?eAb^A#*=aEw;Ncb|-voLGPb;`kd(3Mwq8XcRx5v+3qz}a895aW+}%1tAa}$eJOh2 zBy5jJ`bXmV7TC(1^1Te{UoBD1xi#ukRy#HU*^G(!;KfnmpdK)32=a~EPbJJW=dFPH l-uS?aV?qcagb+f`<_{U?Ub^`ZJ9+>B002ovPDHLkV1hTBC_(@L literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_nft/00008.png b/tests/ragger/snapshots/nanosp/test_nft/00008.png new file mode 100644 index 0000000000000000000000000000000000000000..72505b8634aebfd591d6d9468d4e33b945598c93 GIT binary patch literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h5*QZGDR978JRyuA_0b;v-3^@7Gb zmEZ9}-yXReUdf8qx$%9`^BPerfp=#abXge~{zYc5-TJL+@82gc)?H-&QNH~J!|G-f9Lnl*iYWNYa^?o z_28<_8fIUepm(>geA%iZn{vO2?}mrj?SH{uJ6-q_G9T;co?o4~(P$Z`-5o_9h65FK zFSoy_SoT6xAmQ%0Yv_k&Z+ZUFY(4Io8RUKoeKZL;AXvd%bj&?iK#lmHM`V!7z+Ge z#yQV7uQ3)*d$oU={Ri&~J+1@yefIwM@&7f^PWkUf_A_5Y-fC4ToVSvA<>GI&{_KRM y|IPRpP5U={rK8I30F_#cNfnGh&qBb1`+pb$Ql_rCAO5%>B;x7n=d#Wzp$Py~9e}6+ literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_nft/00010.png b/tests/ragger/snapshots/nanosp/test_nft/00010.png new file mode 100644 index 0000000000000000000000000000000000000000..33d608bb92cf2ced27e89843177b32a87b8c2c78 GIT binary patch literal 301 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h5*QeQk>978JRyuBIS*W@6;dSPCt z^~=BdE594bx9}}$n8g#S`@Ci%$C5o)`6mMPKR6YYy>{#I$A698ze~p+xO#UHx5WmT zms_u`70z1wH{tc6!_loL?3iDzxud>gz4_zJFO`?{SEd~>>8g%+-*!*ee9~I~&tDV* zndNj3|5_ltddhDj)4RH!{G0S09&C-T?w vN};V^{Fkv$T;nwH&EbhhxPe}Ug8$F>kAIrFrrv^oD@erC)z4*}Q$iB}7X*Vu literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_nft/00011.png b/tests/ragger/snapshots/nanosp/test_nft/00011.png new file mode 100644 index 0000000000000000000000000000000000000000..a15e6bed29481f1a29d0b7605f564885cd79a37f GIT binary patch literal 302 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h5*QeQn?978JRyuIO=*K8ob8nFG9 zZ_R(@pL($iqF9b7(HBZHE18+4nx5*GC1svKPMZx#!aU1BoXt y?LRYj#nO61(;1%PvrgLCOs-%A`V|5mKdIo z^l0Vg&hNYj`YWGiuZWDxImG_kfPvvaTFm-|FLxh{tb8+9f38JMt8+_~ZOJqlTmAL1 zu~(L#DE|5SeBOziUp6_a9c*56-j+$8WDQ-us&2l~{5z#ds{`sPpBjHW_VdQCBdfRF z?)sl~d!GEW_}zYQa_<`7OfA|nH9a@hk^e@|a)VDSOA^i7w~OAKp}pEgY5v`Nz0dE( zytz}dWsmCl|J$n8msuAZoZ0<>-E0!C?c>Ru`aBx*87zOyV_j|~QMqu^iITiSpR%sL zer>aU;iMdo`ITM?$2K4BsdLyo`Q_i^A-iVXtCvXLAhh~|mz%mVTkyrbz<@&r2TcC2 Wm8yslU7R@+B<$(x=d#Wzp$P!+C&Hls literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_nft/00013.png b/tests/ragger/snapshots/nanosp/test_nft/00013.png new file mode 100644 index 0000000000000000000000000000000000000000..89b78dba9daca79bbdc5cdf8ac92ce821fbf46fe GIT binary patch literal 432 zcmV;h0Z;ykP)fV!9&H?f*TwQL!6n1ey{6006Mu&-zDh+g1*8&ZEeb zW^dsc{Q>4E*}qrni^y+4N-5_o{_HPEzel(61v27BP`zxK$KvOC0Kj*eyccBew6sC7MC#{%#?rJu zWnb)xd)mCgmfvWJWY6o>X}BXo5eZyolY9~^H;*oA4aAR zrM~@%*+0+x4siZ^>NeRI`ky7J)@xC?NAWt?CvZmjVCJo zDK%m@u6_njo5%BW;523!CYkInqokVbFGe3kq6$UgJsgSj0ssI2000000000000000 ar+EQ&UTi;1epT%N0000H)9Sou?uP9KCFrKKF+B( z;dDNobK&3DycRuuINRTY)L*Pr0$5)Gc;%0-)0rFVUG7c?ti&voc!}a z^m_?^%x(_-t=0+IU5P!nJei>HemJV%O^AWi`bi7^_y_{Tz^qV00000 f0000006^Cl!j~X&&{~Bn00000NkvXXu0mjfCg#d| literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_nft/00015.png b/tests/ragger/snapshots/nanosp/test_nft/00015.png new file mode 100644 index 0000000000000000000000000000000000000000..a58590b988714545e7960f7f400f360ffc5de41f GIT binary patch literal 382 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|@9hba4!+nDh2#bl+hG9*4k? zQuR0Wo;SViCn%lTo!KM1sAjk5Y(@*V#~rbuHu`7A_H&AxgwHf8y4cdKvg$JbB4n%ez%sZ31aBYd&Oo@(7)_WJ(Znl2o8^PD` zFE`_Y-IS+Kdq0Q2-7r_C=}F(5ev`U`H~)QH|9$1FHE$YjoVax+e0xei%S`SAGlOm! zp7{T3NA-mVOYb&rdU8MdyIHo@@7cE*uj@I+Zu{2i)KMSV-E~ozq$nFu%&rLm00000005ujYjps}rIhk^ zjB#NkXSkeza_CDQr@`L@uJ1u@VtxM4u7eynhaMDXY4j5BXTvia$h4hHA}C?O{ZP~ zQ#Rz^-G0>uc7#29^&G^0b2n_e<&PSxY^py0>oIXX8M?;plYOD;+~1i1c@>Aj%Gca7 znHo>4P42p9ulJBEPuKH+YW-FJ+qI~@uCwwMGLaqgD)PhbTmkAK`hS8j)VQ?mC3oo= zQ~TzrMkTvD7I{9-;SUY~0PrF1^{a>jX+f(#V<(TYgw<+^e|B9cG>SH@X8om`OARMt zCmF@gP@I^p;$ka7^XuIc5MC2hqe@dXyR>ex(1Hn=ZYb$*Riai^4xTUpXYSjF4>HRW zCScU99u+v7fX=z)SsU0ltAr1fl002ovPDHLkV1mYF B;~D?} literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_poap/00001.png b/tests/ragger/snapshots/nanosp/test_poap/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..a9deb912bea305dba41ca7a504ec72e5b5fd1fac GIT binary patch literal 530 zcmV+t0`2{YP)UH}U_x4 z@tpJEO78PDyMWW|sqY_pEZ?&WxQHF>5!Fv|J0N{39&z#62;2d2MVAEb0A|2s;)*T_ z+yTsh$;1_167MURQ1hplEsbin>Y(|ynmt+~vW1+pj4Rc)NQtWxWKu`&h7uLQ7aMAr zWBZ_lmLf_;QmxON$l2C>#g7+oA1U>p7gWYbt29LpH9Vyd)u27r6>enM1R_S4y;MP%-# z@Qax>O#uJ^0A5Vr{|6j-j-PUq*TY~4$uP%sx;4AUxEXN7u#{J+h%z#(zNWPns%C@# zyk;||o^xgBdqK(xDW7xBj$HTUh+ zo5XuF&Zi9LC>!jvI-slB`H+l?XLUf@|7Ggz<<%X&dM|V{BLDyZ00000006|`2lQHr U3RG4xf&c&j07*qoM6N<$f`gm$A^-pY literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_poap/00002.png b/tests/ragger/snapshots/nanosp/test_poap/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..d68074544480837501beb5766fca462f6a52e0d4 GIT binary patch literal 361 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|=-xba4!+nDh2VE?=_(PebCi zH)?zTPuy|3ZiYz<|3R0Gi{F1foe_0iX;&Li4};G4jlA;7S8@es%D&*3$TnAY`|E6p z=*|q^Dr`t)~! za&H0~t~_|Wuyn!pttYMvg-rE*veJF+c1Om0M-R)+igo_EF{dod{nX`I|972Zoe_Uu z+GMiNx9KaF%zC5m#;dcDEAo+LPUja;Kp}(6e;C((y+0>ya|t*&JYD@<);T3K0RS?@ Boj(8o literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_poap/00003.png b/tests/ragger/snapshots/nanosp/test_poap/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..d457393f706424941f9770f418f919c42d561be9 GIT binary patch literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|Gw}pJdo(;RggkJx*&0M>HlhsE#T^qB z&JzDiwhyYegge~&nv`9h@2zq8m)vf4pF^{soO^R?m!sOR(-sTWesxc5_6S+%FLQJpa1sgH-z8J?#(``3)4U1*wM-D=?yT)YCzsaifgqP^4RxVY7-C&%Id9Bcnb0;2r94!qpvwMRXrpOu14*XTmT zgCggEt>&{n03scqAFsP3U1(Or$NKWE7a@cYLdbG*H8d4TOc?;_HkNcmZ`zEs#4}8v z`{K&M-PPVjHOQe~{0ZD&2LeMdy0|bE3(w~GUaBfrRf~J5mLje(j{}>Enmu$LYsC?- zxp_Y~YC)#N=0!q>GY#-4+iFMg?!pXe+JAKbaOr&PS%0B&0A};H>2j|cYKrxmbovj^ zU{y3PL}YnYi*ZM%=Q<#r$Q7Wc9+GgpMXL93ZDB*6nWm|!VMt|fpavt8HNi4s*BX`3*wR;LW~lAurX zcDkgLSl%-=5c98Iw}G4)ca@5-v3WghQ@s`(_e$eQSPUcV#^2i+Rn0SRT3c@+#?!IH zT9D2g+Qj2n_>gy=JWCLrvQri{LDEqa08mQ9NiD8jMj#KFA0`!Qs7Q0QQwFKuSOZh;{G*1t;HB-eDkzn79rx%)PqN##_GOGD8xbV8VoAp1^3wZy0T=F~NJI;q- zpUubn_h6=uv=D5bCYA@Lpr`z^4*bsN`{-7l5&-XoZ=EaFzNkB#_1n-uyB6Efv?j8- k{Pt}qA%qY@2qC4Ee}`W*d%(VAV*mgE07*qoM6N<$f^F+;d;kCd literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_poap/00005.png b/tests/ragger/snapshots/nanosp/test_poap/00005.png new file mode 100644 index 0000000000000000000000000000000000000000..1c86e8202d0a00736775af17f644acfa7f6b0f4b GIT binary patch literal 629 zcmV-*0*d{KP)&kVZp_)oqH(0k@t+hHM zbzRGo=66MypwT3IQEjxb$)M%hS8<6u_pKjTX z`n8zTG9-BH1m(P`ud(=tATR_R00030m(^U*5JpgC{{0dXgI>!v|ZD=Jm;j4rLj@O zE7z#bGh^~~UWZb1qD(~H56e3CPscbBl!_SI=A0h}9Y_6^RD|4T_S_0pE3fa9OK0S* z+e6qg1Il~$zp7E0{Z(JuRh3@aW$crA$9X^}ExW53r$czniC7QD{P4wbhB?dtHR8*) zrhPcBRyLNQ_ByhT`$MmwHm+8V$({lL000000000000000000000AQLw`)Vxl#%ve{ P00000NkvXXu0mjf)C?vx literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_poap/00006.png b/tests/ragger/snapshots/nanosp/test_poap/00006.png new file mode 100644 index 0000000000000000000000000000000000000000..cf2294404fb8ef53cc5e2c9e9247ead4b7f941f0 GIT binary patch literal 925 zcmV;O17iG%P)gvv;Kav=lVmgXz~Y&RT0O?Hjq9%oK!S4tv%>uUp{B*#KD;c-{|- z4T(JI({`K*&!wQxAtiFN!awav7%)r%MNt&R z>rB1_n0gCHiJ=yGiBWI7qL5L6Kv@a&sKhx^FwgY%9{vMgjDt(SNzy+45swx6FeD~L z^w``~MSxb}wQSZ-!&R2@$^H;)>}o+-?_T7NdP!z@)pI}@RGC5Qnl_lGSuT;-2H4pS z&jg{x&Ba3Lr0ZM^@C8(#5>9H_W{0C$mV^&exf)V2B#Q+x0xm4)79vdFau zw_=x6U}Y$H?7lEY!oNe>yHnZ}|hilQirq7(T009;tu}~-A0z7AjQbcn!PVk*!hmOvZ8D-(N zebBEa?!35&vM^!m-qhi>)$iGc$>~u(LJfT;+?HeiR?QV{fLDr0&0T#}+E;_B@d{F} zTs!>Y&t9Eoq0|3&I7|iXIa3rxQ4~c{6#t%Iktl>g{fSdE00000NkvXXu0mjf>u|m~ literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_poap/00007.png b/tests/ragger/snapshots/nanosp/test_poap/00007.png new file mode 100644 index 0000000000000000000000000000000000000000..86740bb5ed8f3a4b2a173abbac5240bd19a8ec9c GIT binary patch literal 948 zcmV;l155mgP)sU-$g6- z$0=t8^x3zW88_QwE*jpicq@zz>6X(|e_9Hfj=^;5;j9%4iFxc;wlT>}L6~U{Np!Sd z>(~w_X8^`60eG$9ox`}yr{y>mj8!~q-HvF2xA~c-sSibW+_OtQD~7CwoJ&EUL)NrS zv6FTZ2AD~pD2k$ZoXI;tZ?^Dub9xtS)%P8*ZPwi&#+9v52 zR5ab8VawXC6acrAtM`v#banOqVQLJXq_WVHOgg#7;H~KHMQSLs2lX6at;XCtnk7?J z)hk~LKD_x3guw!I8}f`kHMmr3Eb%N%WA;=aQ|(#0Aj8mXNfG8@rYd2Hq#xm_7jB@GYp2b72rj4S>KgN{VUu-F=6r%cEvdT zgTq*xNzta~>;0sWZZJl&9Gx}eJkdDOOS-`r?f~dCY9>~&Z}B#ie1aAgahy1K9;V@- zBrA-fD2k#eir>we8rsWAwGT+4t6#T4U{k4*@#w)Tub#EwA%oE@d3|(AP-K)PKA5nh z%U51OIr(JAK8TH{^Hq(BJV4><*fm%mX7yOuo>0mQs9RDikbK3pcMCr-UUa6e!Jl}h zkyjd+8}sR zaC!iK1fEdpmFxcHrB-$TNVV^N{SP$en{8SGT$_7hvO4Gm?1EvldQlWbQ4~c{9OWP1 W@ahT?I|*k1000022u0JL?!^5s$-VT$GuDDWi4x;X-u=o%MHECOa~y;aLI@#*Ea%HhP>eA@hY-@w z5Q3L{Ut)|&H=(wua};9{b+Uvlh%uI0ZZ5nkP`|`=ytMk67g42L0b0@IR;6>b{IMMH z@qcZJF7z8AYC!)5d^^g*O6OSst2n$k{+lot8GFxdwJ~&T%)Sid>pCspI*X41$~m2p z6JHFgQLZYZC~YTgkE$(Qa0@|sq5{zc)6{M9JiY=KzPa< zEL%bdc?Q-!puAq`<6ZZNdDt#!RXL|_a2B<-=6E30>_xZ7$z+riT0L^%BeSKbRbONU zPSq@naHqH}e(350-jwt&V))8~8>WYrf0ZLSC(liw^oc6?kVy@qOGno zgGCfF0PyaCo|a)(TeE&3(g4N?n;xnGC%p{nb7QL^mK@ageIePhA-fC&$hYtIo$0+j zPx%Czlg!y)MrEow`-`Cs?u1e3PCAoJ8(hlgOz#fgmY)mGN4^(V{WtVda6WRMJNbtr z5;!+%GQ$)9ma-Kzz|GJAH}jU8ux2tXN(do@5JCtcgb+dqA%qY@2qA=!&*K+3qD(1k SJ|_nN0000fV!9&H?f*TwQL!6n1ey{6006Mu&-zDh+g1*8&ZEeb zW^dsc{Q>4E*}qrni^y+4N-5_o{_HPEzel(61v27BP`zxK$KvOC0Kj*eyccBew6sC7MC#{%#?rJu zWnb)xd)mCgmfvWJWY6o>X}BXo5eZyolY9~^H;*oA4aAR zrM~@%*+0+x4siZ^>NeRI`ky7J)@xC?NAWt?CvZmjVCJo zDK%m@u6_njo5%BW;523!CYkInqokVbFGe3kq6$UgJsgSj0ssI2000000000000000 ar+EQ&UTi;1epT%N0000H)9Sou?uP9KCFrKKF+B( z;dDNobK&3DycRuuINRTY)L*Pr0$5)Gc;%0-)0rFVUG7c?ti&voc!}a z^m_?^%x(_-t=0+IU5P!nJei>HemJV%O^AWi`bi7^_y_{Tz^qV00000 f0000006^Cl!j~X&&{~Bn00000NkvXXu0mjfCg#d| literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanosp/test_poap/00011.png b/tests/ragger/snapshots/nanosp/test_poap/00011.png new file mode 100644 index 0000000000000000000000000000000000000000..a58590b988714545e7960f7f400f360ffc5de41f GIT binary patch literal 382 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|@9hba4!+nDh2#bl+hG9*4k? zQuR0Wo;SViCn%lTo!KM1sAjk5Y(@*V#~rbuHu`7A_H&AxgwHf8y4cdKvg$JbB4n%ez%sZ31aBYd&Oo@(7)_WJ(Znl2o8^PD` zFE`_Y-IS+Kdq0Q2-7r_C=}F(5ev`U`H~)QH|9$1FHE$YjoVax+e0xei%S`SAGlOm! zp7{T3NA-mVOYb&rdU8MdyIHo@@7cE*uj@I+Zu{2i)KMSV-E~o0{{R3E;qMeEW(A&?Bs^XCz@yyjhBQoYBx}G{^Q7! z)=IdPQdgMG_G=^OEce%9p2EX}*DC#X`dRv_i{a+sj9o@DB3EORb&c56ReIBJI&}n` zveEw?wyPGfBlOhr9K_x^4BIaGrN-J9RiFQPoH(CMjdA;8U#PkE2PdE(#c4A0b#9tW zjptpN+O((EJ^Go)^*o@Nf7SoGCbi%5Y<~+mksb3Y{g)k#0QC_5R}iKOORrje6;Geq zH%~RHzPV$O*W(=i;s5{u03KlL_k#SHRA-_TbJVn@Gn2aNVaBZKRK(4k$)cUR*tYe{ zxlK~VQ~2tT9%?wT7>|I`-s5BIDQGI8XxtAN84K)u)k|C71D=2r00000003_E4R%kG UkeP1^=l}o!07*qoM6N<$f;8OZ$p8QV literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_1inch/00001.png b/tests/ragger/snapshots/nanox/test_1inch/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..a9deb912bea305dba41ca7a504ec72e5b5fd1fac GIT binary patch literal 530 zcmV+t0`2{YP)UH}U_x4 z@tpJEO78PDyMWW|sqY_pEZ?&WxQHF>5!Fv|J0N{39&z#62;2d2MVAEb0A|2s;)*T_ z+yTsh$;1_167MURQ1hplEsbin>Y(|ynmt+~vW1+pj4Rc)NQtWxWKu`&h7uLQ7aMAr zWBZ_lmLf_;QmxON$l2C>#g7+oA1U>p7gWYbt29LpH9Vyd)u27r6>enM1R_S4y;MP%-# z@Qax>O#uJ^0A5Vr{|6j-j-PUq*TY~4$uP%sx;4AUxEXN7u#{J+h%z#(zNWPns%C@# zyk;||o^xgBdqK(xDW7xBj$HTUh+ zo5XuF&Zi9LC>!jvI-slB`H+l?XLUf@|7Ggz<<%X&dM|V{BLDyZ00000006|`2lQHr U3RG4xf&c&j07*qoM6N<$f`gm$A^-pY literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_1inch/00002.png b/tests/ragger/snapshots/nanox/test_1inch/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..82fbba418b8fbddceca090ba5e95952ec29af2bc GIT binary patch literal 756 zcmV>^4yMNU5myZwk?q&yM@`i{pmgUgcE21*nS`@;k8-(Vorfl`h+w0FTyXLATD(QCiD zFvnJFE5vQ*ly^WiM1;a0S+Z3bKBI~d3xI10A%qY@$TFO4sC#G=$48yXsp_fbWP~19 zOjO#cs@m+f?r23qDJbq9d8?U#mWAQ%5N(KMn)}wEQ zAdlpNkgca>kPwk8r#Q8hh!*;*1JkO;#@6S6>WFpR%n{rjaWy5WD`#0;sLEwX^v zr5vsqPOFJA+re;VHdN;ebK&xhanClwqL3+dYDuA|>xCgkg@i9X5xXNB*B>Jx2pO~n z%nm*Z;!8adJuh&LBfDY50eakLCK?BL=);3S-!W7xO~nr!F5KheSSUC2h(QLm$=uVb z-JN^Hv`@wX-Z6v_LI@%Mhi$)<$7x+FCxSyDE7VdQEwt^}{(1z&X%}cQ_~nmE@0Z#@DyK3jZ3&hr_B^?@QS3Ydr@Y mLtDX}$528DA%qY@Ci4qhUq*mMYg}mn0000RGtYLd`~u(OfzMi6;`-8sQ>Wg1Y{hsp;@ZP?{qIzd z{X168IqBA?-&t=azm}Zvvj4Y}WYJuP*sN_cyk6#qEzFq+w5mb-+uaLE`R?-Be^zV10`oMy}N@eVNRgQC657unyd!WQPx78w= zdEZ{|jj6f&*zPQ4`Poyq!;|TboBbQ_2`4AVZql4m&-W#6^QQMEH#^M^{hq#hj(NMI z+ONwZPQG8%mD#7QaGECNsK)Lr7zcC*GDw(Vp7-QneSuA}F-X|c)z4*}Q$iB}&SsbS literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_1inch/00004.png b/tests/ragger/snapshots/nanox/test_1inch/00004.png new file mode 100644 index 0000000000000000000000000000000000000000..3a6db3d755f9465e9e9ccddb16507259c1982936 GIT binary patch literal 450 zcmV;z0X_bSP)F4rBsp+LBtRM004mPe$G~(Qo6Qdj1OM( zsjGdsl+r7&YvbGeo&EtuX6Mj_rIHw<=nDDnik>&-_h8)>S6F#od7;Q!#~R+V*6Ekd z+RQ}{FmKiH9*vI4Jgl*<-m}(m(EzXEBBdlogM7D=*Vq{1<}VH<+Dffze=CmYwZxUt-sHtpMPN3&&4xf@+Nv}tUE$Z zLkIp$ec7_qRePtQT{FVl(Wx?B?ya;duA6wBd?fm8#gIvV8zXAc-;6dmiBagJn~-UP srHB^*000000000000000003C#AHp|fw(Q@T-~a#s07*qoM6N<$f{;bh!TfV!9&H?f*TwQL!6n1ey{6006Mu&-zDh+g1*8&ZEeb zW^dsc{Q>4E*}qrni^y+4N-5_o{_HPEzel(61v27BP`zxK$KvOC0Kj*eyccBew6sC7MC#{%#?rJu zWnb)xd)mCgmfvWJWY6o>X}BXo5eZyolY9~^H;*oA4aAR zrM~@%*+0+x4siZ^>NeRI`ky7J)@xC?NAWt?CvZmjVCJo zDK%m@u6_njo5%BW;523!CYkInqokVbFGe3kq6$UgJsgSj0ssI2000000000000000 ar+EQ&UTi;1epT%N0000H)9Sou?uP9KCFrKKF+B( z;dDNobK&3DycRuuINRTY)L*Pr0$5)Gc;%0-)0rFVUG7c?ti&voc!}a z^m_?^%x(_-t=0+IU5P!nJei>HemJV%O^AWi`bi7^_y_{Tz^qV00000 f0000006^Cl!j~X&&{~Bn00000NkvXXu0mjfCg#d| literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_1inch/00007.png b/tests/ragger/snapshots/nanox/test_1inch/00007.png new file mode 100644 index 0000000000000000000000000000000000000000..a58590b988714545e7960f7f400f360ffc5de41f GIT binary patch literal 382 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|@9hba4!+nDh2#bl+hG9*4k? zQuR0Wo;SViCn%lTo!KM1sAjk5Y(@*V#~rbuHu`7A_H&AxgwHf8y4cdKvg$JbB4n%ez%sZ31aBYd&Oo@(7)_WJ(Znl2o8^PD` zFE`_Y-IS+Kdq0Q2-7r_C=}F(5ev`U`H~)QH|9$1FHE$YjoVax+e0xei%S`SAGlOm! zp7{T3NA-mVOYb&rdU8MdyIHo@@7cE*uj@I+Zu{2i)KMSV-E~omBiSJU000000002Mr|@;RfO%q! zseByAjY?K24=|Ixp?!0QTt+h|R5 zkJCT+T>(X3d+hoXf2W|pG4vt)k3b9n0002sa>fG2m~o+-C-Da511^e0<0W!>ejT~9 zx073Hk#LMLMVLkTEF8y?Ur%Nh<8I@rN?i`G&{qtGi-$A1O^%{m)Rxy(w24uA(Qh*1 z3fNMpKf8WK16`q|mZcH>Vpq0q`6FtrO~vPb7B=oDLu*{0>^D-({muZ?t2hj1zV@cc zL_IB=3Kl0|BAnwHnqR|Y<&yah_3lA>Ob3A0b&#OR}hAbbIw|I6-%G$ zH&0Pj?QTwaYG?Y70{{R3dOnSLS<-40w`iufus3d+ zYpuw=+w)7)j0Ixd1Yw?E6O)3i@T91?m;|k5*p;lr=hp^b#tffgHkVB_x=?(HW{L}= zao4GZ;Z%gK;0orFpeyZ&i$Gjf2Ovn>!gl78prXN|28+$m7UH}U_x4 z@tpJEO78PDyMWW|sqY_pEZ?&WxQHF>5!Fv|J0N{39&z#62;2d2MVAEb0A|2s;)*T_ z+yTsh$;1_167MURQ1hplEsbin>Y(|ynmt+~vW1+pj4Rc)NQtWxWKu`&h7uLQ7aMAr zWBZ_lmLf_;QmxON$l2C>#g7+oA1U>p7gWYbt29LpH9Vyd)u27r6>enM1R_S4y;MP%-# z@Qax>O#uJ^0A5Vr{|6j-j-PUq*TY~4$uP%sx;4AUxEXN7u#{J+h%z#(zNWPns%C@# zyk;||o^xgBdqK(xDW7xBj$HTUh+ zo5XuF&Zi9LC>!jvI-slB`H+l?XLUf@|7Ggz<<%X&dM|V{BLDyZ00000006|`2lQHr U3RG4xf&c&j07*qoM6N<$f`gm$A^-pY literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_nft/00002.png b/tests/ragger/snapshots/nanox/test_nft/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..1ac2ab07753bb344d9944b4c3935fcd2921d373e GIT binary patch literal 702 zcmV;v0zv(WP)c&zur@%X+Ij*PoU?OAnNg* zN4T4^dIr3nrv^aEjK}fR0F?i==%L;R00000_*iT;)ZFLw$(CoqpczYBSToEP$uRcJ ztaMiHE_xSX&a#);$Ly(hQ(B}KtV|@?tlvwhbEum>Ls$#)OI^xB&XYWO9cJ{+bD_LC zP>iEaKgW!&I4QeF+ZMfwbQf$;&H2Lt*tX4i`oC6%pQ!0Ot9jpY87oIi(s6^i{4cLy zA-WUMlU`vlZlCnX1CXT60J`)%1xHF!x`*ovo98o=HPtlC(^7+T&A(x6M5(A$S7q|GGnDg)R$w? zwIEtIsEPZraFNGyBuhLxWl|Pp@suMi08S~*k7;o3XjA5*OUBbbB)yPT6Im6+DWkG)l^4=fqgnb#dhcJ4Q+}vD;(X|w zv-^1e9@x@hHadr!g=N7Kw93CK@Zb6UJ{rm@0gxW}rnO@3gSwY9e;cZ3*UdK6?CIHD ke)={P000000Dw};FJ0{81JjCfLI3~&07*qoM6N<$f~{Cmb^rhX literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_nft/00003.png b/tests/ragger/snapshots/nanox/test_nft/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..330755f9fe2708a83b300dcf1079d27ce076d363 GIT binary patch literal 344 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|^K@ba4!+nDh2#AYZcrPebCi zH){9(d+a?e=fk7DF6+v)Q~zx(o4MZVFnTirHQm{}$hx4XV}+rMENfF$G&$5Sf9N(ztI1C+7f>U>5nm%9`hUCTvA`4`)lX2#xI;2`>)!Ee*v2F zLBM_b!H1`xsjp$~3zTdgI z@0z3gedeBz^Lc#3P2D-Y9d?%{hc_7C{%d=E+6I;{G9vy~j7pD$rA)i8eSVn5-t&Lo zEbr;xj?G=X&CmSM>tOAx!Bu(}%70kLZFk_A*RaVd<++B((a$^ocAP(W;?e30C(N3B j`0`vUKwd-w4Yz9;d2ILh)?KSF1qpk)`njxgN@xNA?RJ!r literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_nft/00004.png b/tests/ragger/snapshots/nanox/test_nft/00004.png new file mode 100644 index 0000000000000000000000000000000000000000..5a3be0cc448c1f4e40f7d3e5b3771ef9c8f6a41d GIT binary patch literal 557 zcmV+|0@D47P)Nkl|GXmri#b>@rI(8l3)f|723 zi_@*VWOliBHX0e^Rd<79FJif?P*UiZ-4uhL4onVDTFAXca>y1ab1i&HHWs@@mXrOJ zmfY|zJ|~S-uttioxGp~hlLHky$>9~AjGpgn5@6rEoO61e(LbS#hAYk}7L2aeafTZD z#l$P1;%FsjnX=zT{Vl~oADElD_9Lub$09qHC8fWY=Fgr7wJ#$ZlM%rp0Z9^J85AiJ zkYipBKr*!oK|~?Q3=X{zJ*UHu#G=t&)zDpo94f3@#HG=y>I^kB z`K@L-#bk%#aQlRn%P4HW_7V3Rv1^bO>SMb#>22=SNR$o#FWJ&U=M`YoAQg5v1%#_1 zw^FZ8t_~?dErQ9MIuI?6FyfPI(T}R3kFxeW-0kysg3c=-c?NwdQc(vr3Z)gDm8zKh vnayQBH2?qr000000000000000z@EGTXu{zxie%){00000NkvXXu0mjfcUbsN literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_nft/00005.png b/tests/ragger/snapshots/nanox/test_nft/00005.png new file mode 100644 index 0000000000000000000000000000000000000000..d32af33c70806464c001b6ebd09bffa085dfbc43 GIT binary patch literal 562 zcmV-20?qx2P)V~n90}{|ooSSWPa7F@}%ZO!(tmW)IK<9`}7ytkO;5Qkgh9=jXQD?qH4ec~<6O?rS zTbyp?C3DENv(w3>sD?Y7dl4&Lg|b4w?4~&U)G%wF^-y|5)?^EmxfXvU8>`(C&)NP; zOUZml%vmQ5tdSxtq03*vq@m&`Yu*sa?D@VX0rtJiIj7ed{TIr_TnR?;U<|d5Gt|&8 zCS3syM=LqYl>Ij8Zz&GCVQJ>tkFa_jOZ-@tl>RI&o;?q0Uq*3EW(*by$dVbBNh3u9 zN<5bXkUUz2AfYnI($FOdUuT$5!>{C)Cg|Pm>9|16?a1dj<^^sj7M!WzmMt zN>z*-gHKE4`KSc|000000000000000005Tr2CHofjBQ|KeEG@b*Gu*}E^XJ`xA&8bs3!TCl2FN)Z)LPDxuvR(406jEAKCJ3*e!V*&!#MqvlBnOD zv$$Y5a)1HMl(q?LgEI_Zb|ac8;>+1<0M8MNFaQ7m0Kj)LCmTw(IkRFuk_{cKZc8Zf z@ZKa3?o6=RU2ny zLthM?0fx}39%amWOUi4U17EQ>a`hoB@5hK7&6MKX;`$RaP_=hGi-8~%m~ zu?GhOJZJo#i`6ZL)&Aw~g2We{%vzsC|L(E?00000007{_KhS;!EDxWdO8@`>07*qo IM6N<$f}(yGSpWb4 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_nft/00007.png b/tests/ragger/snapshots/nanox/test_nft/00007.png new file mode 100644 index 0000000000000000000000000000000000000000..f79d5f8db9d17edd0e4dca2637112986789e3ddd GIT binary patch literal 651 zcmV;60(AX}P)MUvl5Jb%j0UJ-dE~bZ{l)L<3h01AKZKyh zH^!(=7z;U|0o1Z31wyuDg!(itLozn zHT1>gJ;1TFl1~|!Z?pWC!2uiAo?QD97LQ|@A4`|QWuf})xlnr<)tHP378{^QAuNL; z#RjM`uMLP~>L^4qlpwPWh9bke@@{CO*jaP9g+jSM-POt+Bv=Q_dO)@%QB_Hql74Lz zHGSIZ+1S-ehfdkO9icVj?qJ%ts3zV|T(= z?eAb^A#*=aEw;Ncb|-voLGPb;`kd(3Mwq8XcRx5v+3qz}a895aW+}%1tAa}$eJOh2 zBy5jJ`bXmV7TC(1^1Te{UoBD1xi#ukRy#HU*^G(!;KfnmpdK)32=a~EPbJJW=dFPH l-uS?aV?qcagb+f`<_{U?Ub^`ZJ9+>B002ovPDHLkV1hTBC_(@L literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_nft/00008.png b/tests/ragger/snapshots/nanox/test_nft/00008.png new file mode 100644 index 0000000000000000000000000000000000000000..72505b8634aebfd591d6d9468d4e33b945598c93 GIT binary patch literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h5*QZGDR978JRyuA_0b;v-3^@7Gb zmEZ9}-yXReUdf8qx$%9`^BPerfp=#abXge~{zYc5-TJL+@82gc)?H-&QNH~J!|G-f9Lnl*iYWNYa^?o z_28<_8fIUepm(>geA%iZn{vO2?}mrj?SH{uJ6-q_G9T;co?o4~(P$Z`-5o_9h65FK zFSoy_SoT6xAmQ%0Yv_k&Z+ZUFY(4Io8RUKoeKZL;AXvd%bj&?iK#lmHM`V!7z+Ge z#yQV7uQ3)*d$oU={Ri&~J+1@yefIwM@&7f^PWkUf_A_5Y-fC4ToVSvA<>GI&{_KRM y|IPRpP5U={rK8I30F_#cNfnGh&qBb1`+pb$Ql_rCAO5%>B;x7n=d#Wzp$Py~9e}6+ literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_nft/00010.png b/tests/ragger/snapshots/nanox/test_nft/00010.png new file mode 100644 index 0000000000000000000000000000000000000000..33d608bb92cf2ced27e89843177b32a87b8c2c78 GIT binary patch literal 301 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h5*QeQk>978JRyuBIS*W@6;dSPCt z^~=BdE594bx9}}$n8g#S`@Ci%$C5o)`6mMPKR6YYy>{#I$A698ze~p+xO#UHx5WmT zms_u`70z1wH{tc6!_loL?3iDzxud>gz4_zJFO`?{SEd~>>8g%+-*!*ee9~I~&tDV* zndNj3|5_ltddhDj)4RH!{G0S09&C-T?w vN};V^{Fkv$T;nwH&EbhhxPe}Ug8$F>kAIrFrrv^oD@erC)z4*}Q$iB}7X*Vu literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_nft/00011.png b/tests/ragger/snapshots/nanox/test_nft/00011.png new file mode 100644 index 0000000000000000000000000000000000000000..a15e6bed29481f1a29d0b7605f564885cd79a37f GIT binary patch literal 302 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h5*QeQn?978JRyuIO=*K8ob8nFG9 zZ_R(@pL($iqF9b7(HBZHE18+4nx5*GC1svKPMZx#!aU1BoXt y?LRYj#nO61(;1%PvrgLCOs-%A`V|5mKdIo z^l0Vg&hNYj`YWGiuZWDxImG_kfPvvaTFm-|FLxh{tb8+9f38JMt8+_~ZOJqlTmAL1 zu~(L#DE|5SeBOziUp6_a9c*56-j+$8WDQ-us&2l~{5z#ds{`sPpBjHW_VdQCBdfRF z?)sl~d!GEW_}zYQa_<`7OfA|nH9a@hk^e@|a)VDSOA^i7w~OAKp}pEgY5v`Nz0dE( zytz}dWsmCl|J$n8msuAZoZ0<>-E0!C?c>Ru`aBx*87zOyV_j|~QMqu^iITiSpR%sL zer>aU;iMdo`ITM?$2K4BsdLyo`Q_i^A-iVXtCvXLAhh~|mz%mVTkyrbz<@&r2TcC2 Wm8yslU7R@+B<$(x=d#Wzp$P!+C&Hls literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_nft/00013.png b/tests/ragger/snapshots/nanox/test_nft/00013.png new file mode 100644 index 0000000000000000000000000000000000000000..89b78dba9daca79bbdc5cdf8ac92ce821fbf46fe GIT binary patch literal 432 zcmV;h0Z;ykP)fV!9&H?f*TwQL!6n1ey{6006Mu&-zDh+g1*8&ZEeb zW^dsc{Q>4E*}qrni^y+4N-5_o{_HPEzel(61v27BP`zxK$KvOC0Kj*eyccBew6sC7MC#{%#?rJu zWnb)xd)mCgmfvWJWY6o>X}BXo5eZyolY9~^H;*oA4aAR zrM~@%*+0+x4siZ^>NeRI`ky7J)@xC?NAWt?CvZmjVCJo zDK%m@u6_njo5%BW;523!CYkInqokVbFGe3kq6$UgJsgSj0ssI2000000000000000 ar+EQ&UTi;1epT%N0000H)9Sou?uP9KCFrKKF+B( z;dDNobK&3DycRuuINRTY)L*Pr0$5)Gc;%0-)0rFVUG7c?ti&voc!}a z^m_?^%x(_-t=0+IU5P!nJei>HemJV%O^AWi`bi7^_y_{Tz^qV00000 f0000006^Cl!j~X&&{~Bn00000NkvXXu0mjfCg#d| literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_nft/00015.png b/tests/ragger/snapshots/nanox/test_nft/00015.png new file mode 100644 index 0000000000000000000000000000000000000000..a58590b988714545e7960f7f400f360ffc5de41f GIT binary patch literal 382 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|@9hba4!+nDh2#bl+hG9*4k? zQuR0Wo;SViCn%lTo!KM1sAjk5Y(@*V#~rbuHu`7A_H&AxgwHf8y4cdKvg$JbB4n%ez%sZ31aBYd&Oo@(7)_WJ(Znl2o8^PD` zFE`_Y-IS+Kdq0Q2-7r_C=}F(5ev`U`H~)QH|9$1FHE$YjoVax+e0xei%S`SAGlOm! zp7{T3NA-mVOYb&rdU8MdyIHo@@7cE*uj@I+Zu{2i)KMSV-E~ozq$nFu%&rLm00000005ujYjps}rIhk^ zjB#NkXSkeza_CDQr@`L@uJ1u@VtxM4u7eynhaMDXY4j5BXTvia$h4hHA}C?O{ZP~ zQ#Rz^-G0>uc7#29^&G^0b2n_e<&PSxY^py0>oIXX8M?;plYOD;+~1i1c@>Aj%Gca7 znHo>4P42p9ulJBEPuKH+YW-FJ+qI~@uCwwMGLaqgD)PhbTmkAK`hS8j)VQ?mC3oo= zQ~TzrMkTvD7I{9-;SUY~0PrF1^{a>jX+f(#V<(TYgw<+^e|B9cG>SH@X8om`OARMt zCmF@gP@I^p;$ka7^XuIc5MC2hqe@dXyR>ex(1Hn=ZYb$*Riai^4xTUpXYSjF4>HRW zCScU99u+v7fX=z)SsU0ltAr1fl002ovPDHLkV1mYF B;~D?} literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_poap/00001.png b/tests/ragger/snapshots/nanox/test_poap/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..a9deb912bea305dba41ca7a504ec72e5b5fd1fac GIT binary patch literal 530 zcmV+t0`2{YP)UH}U_x4 z@tpJEO78PDyMWW|sqY_pEZ?&WxQHF>5!Fv|J0N{39&z#62;2d2MVAEb0A|2s;)*T_ z+yTsh$;1_167MURQ1hplEsbin>Y(|ynmt+~vW1+pj4Rc)NQtWxWKu`&h7uLQ7aMAr zWBZ_lmLf_;QmxON$l2C>#g7+oA1U>p7gWYbt29LpH9Vyd)u27r6>enM1R_S4y;MP%-# z@Qax>O#uJ^0A5Vr{|6j-j-PUq*TY~4$uP%sx;4AUxEXN7u#{J+h%z#(zNWPns%C@# zyk;||o^xgBdqK(xDW7xBj$HTUh+ zo5XuF&Zi9LC>!jvI-slB`H+l?XLUf@|7Ggz<<%X&dM|V{BLDyZ00000006|`2lQHr U3RG4xf&c&j07*qoM6N<$f`gm$A^-pY literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_poap/00002.png b/tests/ragger/snapshots/nanox/test_poap/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..d68074544480837501beb5766fca462f6a52e0d4 GIT binary patch literal 361 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|=-xba4!+nDh2VE?=_(PebCi zH)?zTPuy|3ZiYz<|3R0Gi{F1foe_0iX;&Li4};G4jlA;7S8@es%D&*3$TnAY`|E6p z=*|q^Dr`t)~! za&H0~t~_|Wuyn!pttYMvg-rE*veJF+c1Om0M-R)+igo_EF{dod{nX`I|972Zoe_Uu z+GMiNx9KaF%zC5m#;dcDEAo+LPUja;Kp}(6e;C((y+0>ya|t*&JYD@<);T3K0RS?@ Boj(8o literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_poap/00003.png b/tests/ragger/snapshots/nanox/test_poap/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..d457393f706424941f9770f418f919c42d561be9 GIT binary patch literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|Gw}pJdo(;RggkJx*&0M>HlhsE#T^qB z&JzDiwhyYegge~&nv`9h@2zq8m)vf4pF^{soO^R?m!sOR(-sTWesxc5_6S+%FLQJpa1sgH-z8J?#(``3)4U1*wM-D=?yT)YCzsaifgqP^4RxVY7-C&%Id9Bcnb0;2r94!qpvwMRXrpOu14*XTmT zgCggEt>&{n03scqAFsP3U1(Or$NKWE7a@cYLdbG*H8d4TOc?;_HkNcmZ`zEs#4}8v z`{K&M-PPVjHOQe~{0ZD&2LeMdy0|bE3(w~GUaBfrRf~J5mLje(j{}>Enmu$LYsC?- zxp_Y~YC)#N=0!q>GY#-4+iFMg?!pXe+JAKbaOr&PS%0B&0A};H>2j|cYKrxmbovj^ zU{y3PL}YnYi*ZM%=Q<#r$Q7Wc9+GgpMXL93ZDB*6nWm|!VMt|fpavt8HNi4s*BX`3*wR;LW~lAurX zcDkgLSl%-=5c98Iw}G4)ca@5-v3WghQ@s`(_e$eQSPUcV#^2i+Rn0SRT3c@+#?!IH zT9D2g+Qj2n_>gy=JWCLrvQri{LDEqa08mQ9NiD8jMj#KFA0`!Qs7Q0QQwFKuSOZh;{G*1t;HB-eDkzn79rx%)PqN##_GOGD8xbV8VoAp1^3wZy0T=F~NJI;q- zpUubn_h6=uv=D5bCYA@Lpr`z^4*bsN`{-7l5&-XoZ=EaFzNkB#_1n-uyB6Efv?j8- k{Pt}qA%qY@2qC4Ee}`W*d%(VAV*mgE07*qoM6N<$f^F+;d;kCd literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_poap/00005.png b/tests/ragger/snapshots/nanox/test_poap/00005.png new file mode 100644 index 0000000000000000000000000000000000000000..1c86e8202d0a00736775af17f644acfa7f6b0f4b GIT binary patch literal 629 zcmV-*0*d{KP)&kVZp_)oqH(0k@t+hHM zbzRGo=66MypwT3IQEjxb$)M%hS8<6u_pKjTX z`n8zTG9-BH1m(P`ud(=tATR_R00030m(^U*5JpgC{{0dXgI>!v|ZD=Jm;j4rLj@O zE7z#bGh^~~UWZb1qD(~H56e3CPscbBl!_SI=A0h}9Y_6^RD|4T_S_0pE3fa9OK0S* z+e6qg1Il~$zp7E0{Z(JuRh3@aW$crA$9X^}ExW53r$czniC7QD{P4wbhB?dtHR8*) zrhPcBRyLNQ_ByhT`$MmwHm+8V$({lL000000000000000000000AQLw`)Vxl#%ve{ P00000NkvXXu0mjf)C?vx literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_poap/00006.png b/tests/ragger/snapshots/nanox/test_poap/00006.png new file mode 100644 index 0000000000000000000000000000000000000000..cf2294404fb8ef53cc5e2c9e9247ead4b7f941f0 GIT binary patch literal 925 zcmV;O17iG%P)gvv;Kav=lVmgXz~Y&RT0O?Hjq9%oK!S4tv%>uUp{B*#KD;c-{|- z4T(JI({`K*&!wQxAtiFN!awav7%)r%MNt&R z>rB1_n0gCHiJ=yGiBWI7qL5L6Kv@a&sKhx^FwgY%9{vMgjDt(SNzy+45swx6FeD~L z^w``~MSxb}wQSZ-!&R2@$^H;)>}o+-?_T7NdP!z@)pI}@RGC5Qnl_lGSuT;-2H4pS z&jg{x&Ba3Lr0ZM^@C8(#5>9H_W{0C$mV^&exf)V2B#Q+x0xm4)79vdFau zw_=x6U}Y$H?7lEY!oNe>yHnZ}|hilQirq7(T009;tu}~-A0z7AjQbcn!PVk*!hmOvZ8D-(N zebBEa?!35&vM^!m-qhi>)$iGc$>~u(LJfT;+?HeiR?QV{fLDr0&0T#}+E;_B@d{F} zTs!>Y&t9Eoq0|3&I7|iXIa3rxQ4~c{6#t%Iktl>g{fSdE00000NkvXXu0mjf>u|m~ literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_poap/00007.png b/tests/ragger/snapshots/nanox/test_poap/00007.png new file mode 100644 index 0000000000000000000000000000000000000000..86740bb5ed8f3a4b2a173abbac5240bd19a8ec9c GIT binary patch literal 948 zcmV;l155mgP)sU-$g6- z$0=t8^x3zW88_QwE*jpicq@zz>6X(|e_9Hfj=^;5;j9%4iFxc;wlT>}L6~U{Np!Sd z>(~w_X8^`60eG$9ox`}yr{y>mj8!~q-HvF2xA~c-sSibW+_OtQD~7CwoJ&EUL)NrS zv6FTZ2AD~pD2k$ZoXI;tZ?^Dub9xtS)%P8*ZPwi&#+9v52 zR5ab8VawXC6acrAtM`v#banOqVQLJXq_WVHOgg#7;H~KHMQSLs2lX6at;XCtnk7?J z)hk~LKD_x3guw!I8}f`kHMmr3Eb%N%WA;=aQ|(#0Aj8mXNfG8@rYd2Hq#xm_7jB@GYp2b72rj4S>KgN{VUu-F=6r%cEvdT zgTq*xNzta~>;0sWZZJl&9Gx}eJkdDOOS-`r?f~dCY9>~&Z}B#ie1aAgahy1K9;V@- zBrA-fD2k#eir>we8rsWAwGT+4t6#T4U{k4*@#w)Tub#EwA%oE@d3|(AP-K)PKA5nh z%U51OIr(JAK8TH{^Hq(BJV4><*fm%mX7yOuo>0mQs9RDikbK3pcMCr-UUa6e!Jl}h zkyjd+8}sR zaC!iK1fEdpmFxcHrB-$TNVV^N{SP$en{8SGT$_7hvO4Gm?1EvldQlWbQ4~c{9OWP1 W@ahT?I|*k1000022u0JL?!^5s$-VT$GuDDWi4x;X-u=o%MHECOa~y;aLI@#*Ea%HhP>eA@hY-@w z5Q3L{Ut)|&H=(wua};9{b+Uvlh%uI0ZZ5nkP`|`=ytMk67g42L0b0@IR;6>b{IMMH z@qcZJF7z8AYC!)5d^^g*O6OSst2n$k{+lot8GFxdwJ~&T%)Sid>pCspI*X41$~m2p z6JHFgQLZYZC~YTgkE$(Qa0@|sq5{zc)6{M9JiY=KzPa< zEL%bdc?Q-!puAq`<6ZZNdDt#!RXL|_a2B<-=6E30>_xZ7$z+riT0L^%BeSKbRbONU zPSq@naHqH}e(350-jwt&V))8~8>WYrf0ZLSC(liw^oc6?kVy@qOGno zgGCfF0PyaCo|a)(TeE&3(g4N?n;xnGC%p{nb7QL^mK@ageIePhA-fC&$hYtIo$0+j zPx%Czlg!y)MrEow`-`Cs?u1e3PCAoJ8(hlgOz#fgmY)mGN4^(V{WtVda6WRMJNbtr z5;!+%GQ$)9ma-Kzz|GJAH}jU8ux2tXN(do@5JCtcgb+dqA%qY@2qA=!&*K+3qD(1k SJ|_nN0000fV!9&H?f*TwQL!6n1ey{6006Mu&-zDh+g1*8&ZEeb zW^dsc{Q>4E*}qrni^y+4N-5_o{_HPEzel(61v27BP`zxK$KvOC0Kj*eyccBew6sC7MC#{%#?rJu zWnb)xd)mCgmfvWJWY6o>X}BXo5eZyolY9~^H;*oA4aAR zrM~@%*+0+x4siZ^>NeRI`ky7J)@xC?NAWt?CvZmjVCJo zDK%m@u6_njo5%BW;523!CYkInqokVbFGe3kq6$UgJsgSj0ssI2000000000000000 ar+EQ&UTi;1epT%N0000H)9Sou?uP9KCFrKKF+B( z;dDNobK&3DycRuuINRTY)L*Pr0$5)Gc;%0-)0rFVUG7c?ti&voc!}a z^m_?^%x(_-t=0+IU5P!nJei>HemJV%O^AWi`bi7^_y_{Tz^qV00000 f0000006^Cl!j~X&&{~Bn00000NkvXXu0mjfCg#d| literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/nanox/test_poap/00011.png b/tests/ragger/snapshots/nanox/test_poap/00011.png new file mode 100644 index 0000000000000000000000000000000000000000..a58590b988714545e7960f7f400f360ffc5de41f GIT binary patch literal 382 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 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_1inch/00001.png b/tests/ragger/snapshots/stax/test_1inch/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..0560e47b7df836d1d94ae0db297a7871f063a0fa GIT binary patch literal 18763 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 M)_Pj`#46&y0g%n}(*OVf literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_1inch/00002.png b/tests/ragger/snapshots/stax/test_1inch/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..82e93431f29debd3508a00ac5b416ef38e31d574 GIT binary patch literal 10775 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 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_1inch/00003.png b/tests/ragger/snapshots/stax/test_1inch/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..392165d4fc71395ae212fb0ca017ebdd204c126b GIT binary patch literal 6218 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 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_1inch/00004.png b/tests/ragger/snapshots/stax/test_1inch/00004.png new file mode 100644 index 0000000000000000000000000000000000000000..339db1b45cb46ceed041751e09147f0574884fa8 GIT binary patch literal 10991 zcmeHtc{r5s+qZm)z7!#{C0VkQCCZj0ktGQ;7)P!bu8$(k91tYxZ^ ziNTnWq={iB>tHO;?fZM*zn}Mbp5J>M@9`Y({5AJ=U-xxi*Lk1U=R7~3^Ll*4#{B5t zVt=!-u^qLzcI74;+o5M{Z0s+%*@2dewjH%>Y)Y*bS4?h2KVKq~@)|A0k5p=*EL?_7{i$%N>@x zU?ObYO!a#rtHRaNzoOCNa~ww(#hX zajUQkhwzJRNeAVDw29oHguu6E9*2eu6cNr|IG1!)cAguvI(FV9=b&@Pc!uB8k0pc! zjK{j(kq$TehPq!*`jDq@Pm1w$NEZ`E`HNd9&te^bXU38Buk)fkeU5{=uNLJKt41sf zZ({YDxK%vTTklxVkwjnNM9!d$v}OVIFDgKHVKaX@w<0iyFiM&sIk)ygM5wHr{k*x| zx@AXQH_&=wLF_>*QJeROiihB>>bz(ZVB`bC-Dk4yFXTl>$)*3xb{!Lgw;KBFm4T5C z?EAgc%@G?=T!lb1s+3g0H#*ClKAr&#Q{X+3u)oV(UvD8T?Xeif*xC6bD&K?M?19d| z?&ys-NE}P`B{r4aI_S;UMp}|BGr+GJ;KQC*`A1>M7+ecS1p2RX)9g3?V$)0raXr2Z z0sJHe!ywEU?8S`L^>w*jaBA=SRRfGCEe4B5vVM7jv*e}!_*c$5 z<-cy`Dyh0qYO}#!O{h`1j$f^u_>D%=&;kzXC)fUPv-527SFvjRi%)aMO-q6G@DZ#8 zTw`v+lZN#u?IVeN&_*8<)sGxw1NvlO*+m8`wZ^M)Ih2S!`_K z)muLScB!6+Hnk}Z`EyKW8(E40r_6=FZTi@u|1G%d_%{^WdU4g(_O>PA)*kU#X`>3p ziEl381!PoHw4kbn_gl=pngYPT$&D(k?QP90p~1K0mwUUrs{3~!x*4;28P>S39dmj% zWmKO!QwIlkcXv|9^fP(s7a)Rmlx(TZSuM)YmgK~Dy$mYcM<`Ze&<7O@&aZ+-Uo17i z>z~TBCVcRrp()N%20AL2&e^|RF+^)UPp{7EzvtO@dQ{fvQQf8O^etm4z#VFoI|AkJ5p5#ar#hf-Z z{hZu`n>(X>#L*$rsC?;!dO`XY^In(kE;x#BckD(l`?m-Pksj98G{D@YtxpC_S;$U| zDmUFJAD3VcV{Ta_OjiHysDt3z-qLHNmkC z4)qfa5`z;ACXzPfspxgg7#{z?v*BQ5X;)(2yLp?4ID=D57Ur3Qo`Fv=!Urz%6)WcV z9Pg`DrEN_Z+dOQFJY`fH7q|(ywA-J;AT~%X)1rjy*pS!KijewaCDHBgJ4U~7Vb2xP z4DdNVp5Xiy3DgNLZ3EBj%fRfY8Z}}nA{O{T z-K09BIk-A8(j~7()}qWXe^+$LBo**}k%v$aG19?--T*7`lLpMBkFCm{1CjrzU#mUq z&>#Uo>lt7fKI*kz*nw4q(=h-}kF)bccV*Ui8MegC0dOlMjk<5WdEauZsrKM@Q0l^` z7!&6U0LYjuXwtknz#B?m12MAaT40z(5(7geM;e#@t*r(;jx5HRxOm?rJ1|C~mm6e_;Hbq14S)S-*u z034MIgINDDnU5W0W(d7X;d}#izZhp7jI$X~_3w%VK;pJs{Hrk(*M{I#+>rK!TAg|P9y%r&xFlOs z52&}9-3Q}FmB8X(*yQ)A(kK+Lk3M$+7%p)vRE5`wFEM#E*Bk8MKs#YS!+y%#1FC~{ zIy@ggn8vMAXSzL!Y0$Mc^?|B#2#=i`S;I9xfr3GtiODC%vnr7dj+S8~%Z$jK- zuM@Pr!5OJy8o*t1>ma%}cw;g9`|NR0^tF8N*-_Q>tbtce$zq`i(2h0?Bl$}HPSJ6mr$vD=Gq5ta)N~4#Y=;YvFv44x8 z4eNcRXmO6XPl2$#IY%L?@s&MGz#x_Vy)|)>;3to2C z0tmp%U%6GLps;2PGPd2dGTD#mdtq#NSg}QQB11AE(-!=o3oxli*rT_4gHCf+Wxz@C&*M}eW=lj@U1WBuG2bYWU4;- zko~Z1pMVMfntWD!ir=ZDWbt6=NzIHyJU4oK1nM(3gp20le?tb&d7NYC;Yo)2UAPsk6iO}hTeD*-z@`NRkewE+fH&FJR0Do#>F~; zMxp@c3v3imDWjgtI7B;xLTT1Ge7wtGsPPybZPM|G*DTH*IPC}^MEvY4iTnLPe)iww z4gX$ze!7pvuq8ko9IOdeuH(#Iu(&$z=6O22j0$h!;R@nST=y}v1G4MKJkud<;PwcE zQ|T3VB+l~o;^rDF9C^^mkOn`)Q{SM){pK)}o=-CsChmD0{w+rvw{ zd)zO11YawoGSt{n{;qNyFAnRdQ~fd*n)j41Zz2fQavyvyW464!Jmf`e+1cwGZmH`Aj2LW2 zoMp6V@xz3EAkD7v8&9hkXE4dclB$WNsHz`h+1?ZPx}>B`HgkOm?e8QNAd;SbKoTKKkt^Iy}@(3A2O*|Luz<$x*Dy}^z`X8arlxVac?tIeA1>WG1Bh+FqC8%rLx*^whZZ}~WDt40~yw26AV=LTT|3zYDyV`)U% zXh&e%C70N>_^vi@=H`Hi?vYX(+!~OjS3hVL4VG_8G4kpV0Ni3@LLV8qvG<9$j`Bf^ zr@7T*yR1TCf!z4`@H8`ZW4y$%HfSfSdjaW9w7@VXTTqnJA9;QPdq+%}0>xTW^mqQn z1I@&gfmg+Z=r;vJ%w0-)d~HzNTS{Phlq7;hpJM4Q-{K7%f40oxkh&1zLr(M?ywb+y zJZ?CwsrRC#dwBSv!}-qf8#OOD6I-O~_i|5szG$~3k#+BP7`6bPvhi+cVA&TyrB`Le ziX6g0-%uJpLrYK7NeElAo{>Oe@_7vYH=RSEWODOH^+}_{)|ZFH^}H+(*FYL%){-Wf*N5VTpldtLi`02kY4GNzZK??PaH+gx$H z7s)494b(F{BkYk^9PET@pSOkMpZVvmpWj_Ea6gZTSRPn4Xgh1;SA2C(Rreu2xcstY z00>=bQ!Q(;C>YzHL6x-tsFx&&-^>m|%yj+b(i&D&j97Q%=OEJZ4)G_v@50UXJWP}7 zZssUo-g#f;|7lgwDWPxX$J3G;YbO=n^BD*H-TLysQxN%2f$cys@hDTl6<4)mKnIX; zF(ap{W>(Km=&8Es6Haq4ibxI? z6$W0F7_=t51K?=yB4@vO?EmW99l&Xy?_H}c;Yzg5{`oR{MjigY@5J12aLAaw)>~i6BR*@(P-{1K8x@3kDZX`?szZ#P{xp%3!Dr;YIpM!oX zDD+T%UP8y80hwv?CR)b0knZG}5JnyyG^WrqgcEdT%|=X|q#SW`;b9VIxsxccrF+jD zM^!RrU&;lCNs!zTJMAT)R#^+@u9q6TE@a(Ccg2L->~W=;sbuqSkz{AXgIr^bT)<#O zLfVYb6kCs>_j{mtkVW>m)Z*}2!tT{7Avc2+G8qDU6WvCt&+7v6j<~}5(dxJkvgGI8 z0C{1HbmQZo@UI$a6+o87%#Y7z?(J1}5hBT(#1Ht2{^z$$`1`^n%(9->)lW~_-bgAxw2^$3TdCXZo_EBQq4PKy&6wu$9L#1T}6+%SgYcok%8rS_rE=aTeWJL z;zgj%^1)7wB5gw$0N$bOU&B8I^-FC`6C;_kl1J(k-9N9L@n7Cb9EEh>{ku7TaZkmS z*n&T^wGAOks^?9FmQgooD=zbU^w`y&=Omo)sWZOOLS|&X%Ggcvbn|m8IU*ET`p#iT zW@L@xL+JQqkn(Y#6MI<4 zYl1;P#f#iF%{wuVX~%XwVq`Aee5H$`-|LV`jD(TFW+OjV&C$%L;vo@v$u_K49+2SS ztQIkn?Z?Rae_CSm2+Vl=Dgu$+InvKVYE+51=;WN-p}^C8o=x9&IC_W-Q`@P6#G^?PICjHYbnZvf3s66WoC*7o@-bj4tK`5 z^Cg|OCR8XaJ}hpz(`Jw{>jfUO4p1aTQ=XPYqrP17AnyulV*g~u0mDkR|F{6V_1*bA zsB$p=o`-5ab!#~~S4bi4N1TyZ|#v}|18{9OcXI|?IbaV8-bpk+WTb+1)O>zx6%l#m&`?0R|6 zQj`|4graPb9dm!}$d|_N9_C8!Fg$WT*?oho>gJ@RiA_B}M<@D@dt?CwjeJ(*>#9K) zIRr_I(${nL`V*fmyAshAD=@^?Pp921@f)T1s!rW@=AxsPE22Idf_z`(--YqdH}>Fn z>B0?x7rRo9Ev+A{?>J}|fv_YruM+`|pcr4V62hDcjP%d!k}EYSqehC~XcBH+->Bp9 z-+NN0@Adxu2K`#T)RC$ABm$sV!i}$}(DWAS;Bh+1qKxbma8P;q>ODOsUjS{ficRt|guf z$j#!DK7A1k{w=pi2rj#mL81*M#HO0tNX;9C&15xBJm_DVT{y-sF52UH_>i^nc=-hx z^5>rL(h$d)gj1Vzd8GatVc~EhgGFAxK`^FdWzk{8QU_U8?z;DKG;~PIjjHL)D6E@H z!qXhtEccJY(e%em0UOuoE6gZBn~e!YK96>^G8iwP=H1@fYL8Wi1~xVQy}vpR)Ac52 zl{=PE!?~U?Sk;7iD(nLd!yb4$tNB2_TFW>S`?MdngS%6i6_)4V5FZ~85VWqIFw(=4sA`DA8MRhit1#P=(8vx56p@mg@Kr=dsxa6XJ}Dm ztFciHN$G-jXfXY-l;VD0ykYngo-rW9j3r;%?xKtaHv=Cj$p+>ji6Mt{O}8OE^Su=d zeo{2w#XT^K;nz{|kc6Hb(vtZ!r5@gpO(>dVsf*0pA@s>DIYm%;x9=q_8WaE`9QAQ) z5%!f1!}G^39o_!V3JBlFp2M<9@)Cn@rLxo1ivEQDOuR&+`#qZS*9enQS6FgiD|U%M z5@~(r&+Z{8w%wnOPsa1lPk&=6KD9DxL1QAULJ`dg!^#05%`F>#4$T^pl=j+@zAzOn zHUHA&8!_Y>&MvGU%c-+ZB(;koxPh@i|K_+#~<_mJf@(*+-x zU87?ze=pa!=z}w$-|izdkI#`IB(Je!_*FE%L~M&=v4=0O30Cq&i!Uy2{RECMxmhR2 z2!a0;hfj%L*S}jeMgB0HSgRp|AV$pY>7r$wSrFE20+rfqw z*<*uWOLs^?l}OrHvmiDidN3>~;hd>`yzNBGZ@QCDS?S)9yDWV)i8txRD1N>2FUIC`c<_DKN_z^|D^D`2LA+N{ebtT#xCB6){ zobg^KhYnZ#fL};7*r+`YJ7#FR=;NY(G=a;+4)mk)OcKx z0-B^~&B0SUrWqwwS$|m(v~i6LMkq0opnbIWM#l9{cdECOnfsl*8+&IWeap1YCNSqE zf2p$YAMlI_lf^4ilaHPkkYhX&>vvCeBqIZS(yV&k#%T>F!m)z`)iT5}ElG_f1s^0MJvOltm=n}Y} z3#b`~`K*ko=}4ti71v^28gHKPwjVr0~}FCAn&ju@C3xK85NS zjv4+z`sVt^xyy9r(DUc4FsuvvJ7uHw#+Mv5VJ3ntHFdxs1NYFa{)Kz${$-H*B54@>;H`e810q!-cxl{3KtD*ya z>@QB>RYbsHDLkA$WhzY@UkvV|=7M;iD-c;eu7%-Gdi(Fr*yCp)3k-j@iV8o6Rn(7Fc1Igxf@ zAl-?C02Ex-qB1MNsGP~#O@bJEa-==qGPP4^P`{pv;oAEFs5#lP>ueGA9{P@o^+?lu zeOLdwe)WW`#Tj4XLiIx`JlnjD^f?lON$}l9%Atgr$Daf>xnNWX<0U(0e2J?(`tJrL&}wf z411qn?sf-oTU4>WIoaK<;#_TR)b>@Ya!;E0^yFapAWw=;Vb~>X1JlhnPx(eY&zoOr z1p1$66zJ*j%f+=HMydiKY;?DWz|iF%XHh=!>`eoP_0iu=9o6yAIJ*QdK$fQOFkZ-N zU5fjGN>Q9exq&B_MNd~rHO3v+d#($*o3ixO3T{$5fL(dihWkcJzFpr6T{*YxFRjnMPZWF>I{zsm*7j zIIYH4;Dgb3*2tim-9Kn+es@S&Xpd49eVSA}3pV z@S2h|+ueVaWB-rq*h!Wvr+>f-*c+~TK%W@QEs|gR)gl&vq$P6@Cc<&hdIOw~A=B^Z zbzn|+@0NOJw)*ZFTuN$foc?OzZTGz{KuF>>#jpO9t^w+~kq~`JfKd6$$CRD*eRsqCu5za0U5RRizgdYQE9lb9s6$>njjs-n z8w%6{8xRZhD-=M+i3&CrKU|(7HpZpFCmLkfd^JF~UL;n^^X&R7$5OKeF!}FvgIv@) zoAhdWqq04gk5RJzqBmqKVLJfyt$vT2ohJCi;6zIvyQ%2fYU~#ho-E-EIz{#ye0D1U zy`^x$&%7|GiTT)}e&E%GH;$y$YC%xt=(I^Si>i@15QO~=B2zTS8{6PIE5ob8@HH?& zd&NtO$_%602>~)Ftm1m?aENq<(52$1_3Tsel_yo-8z&ppb(x)!$7$y1q__$`*R#@2 ztLUA1=3T^bySQpa1>gXSbk&J;Nl@c1#oW+x@{3q?jGlz7vPG?NQulWHPuYZrUeAKe zuWDVpoD5Yk5l(s8K|)wr%mw~FURFjeGEbza0@dKE>iuW~Km<%Zc0_Dgb|*bswuY=QHy%Wi4tuX4`?DLd`D31$Wk+mTYzHM38&f{&F28_EGfxRHj+TF#zC!Kn$#!d?rod@fF z)q>fN2rR@I=~{bs-D_hLxC0w>SJ^JIx0|p!*)l*Vad%?qvB0ya7_Lieoum5iOa_K0xj09;>ufcYQ48q&pcMo4 zf4FXKeco-K$oZm$y<1knZ*=x>9TdsSqe_<~2BQuwLOva>D=(Lt2!hEvF|NKip16L* zhjYw!8v8I?k~~U7pZ6a@2=4A=h%7n2_v-Kqg@5unOS!J6jQXVdtjbdEIH3QhSh7R* z)+yLzTQS7#hNCfCjs_Bc1PkaH)G|Nq7bmH$+b z`TrgMU5A1G=UMXU{)LPj?+Hx$AvWeNa1cj>cN)UW_AXOi`rmz!>c48cPs7^Q|I~x` zkOxH&kUKcv%a$jVv7;24~OV8pmj< zy)(oBnkn1<+mmj8US+a-HU+4Pg=^S!cV5G#N>NtdI5id!%S4FZ{A^f z6q|JqUznbR6=5+3TRc`og`4nK`U5a6G5E2uP9g0ycmC3Af3d-^PXluasT9fSFVUur zm{$mdBlsj5qE;ZK;6!ZMy1Z(@fDC+V>e0|vFLqO9nL*F%HUGJh34#!1%&fEfnDMc2 zfb@Bvcgd)DgA9y?Kc4O9G1V`ASl+$nViAvki4roz7kbmdbG3-crKnmJ-lqU$dVCqlYAxH%*B(42l=6~nBEMfk-MV2~r*b_<(jxm?WITp! z^#nMXLs#-I1~~O@Sk&m^9ld7fR}ms9H#x@6YMiZf?H9jIfykj;iAR&VgY1=HvslkC ziR(x+yPs$z6>gDzt^4Rv3+rsz3{O$Zd}{jyBoymE)-w#$=h8Pj6E2pj72wv?67Z{w z%GpTZEZtHSsV@1YkkgU<3SBp&z+!n^W24HPGbK8=8BLX}g_0m)V?8(0y0Fa#EH;l=&GlqF(6;XC?EEwiV_E)i`LfhIbK3ja zfOEF2=C2nnTADtQ`GlrDksH0+93xw*C*A-&~l+rF=ZF6sMilA9B^w!5FIxQRG z(k(vG)-S$IBi3salTKpJ=>|d-%#PAb3t6lYZJmj`?XI$7QJ7LuaLDu#zam_`E* zwHdkYHY8ErP=!!pvAYwreE2>mx@l-EkE|0bzVE$M%k0NF&WtPQT-Q?Pk5`og}dyi0xp_i_O_y4}_J z?Hs)9mTsd8&8;$LFVy~ZI7HVXdw%uYJ0uCAaCAMPCF-YM27?7-H7&kh^T~Vp8W;R!lmpzjH1O6YjA^-pY literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_nft/00000.png b/tests/ragger/snapshots/stax/test_nft/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..b0958508a753debdff51ca0b9f0559a645a3dd75 GIT binary patch literal 12738 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 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_nft/00001.png b/tests/ragger/snapshots/stax/test_nft/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..9c132ca3527d19b8aa8ed416e5a3eb42a8a9b913 GIT binary patch literal 21556 zcmeFZXH-*b)GZtpQ0WJ0(p5@;(7UvQ3P^8)gx*4t-a9A=96>>P2Lb7!1pyN}NDW20 z7@Bkl5UG(8xSR9dcZ@skxMSQq#`nJeK7NLs{p_8+)_UfebFTePS6h{Wl!+7s0#T?v zf2I!t-S`Ls-TZXtCh*B)ot8=v$lzS6XtYSb9{=P$f^R3mB-));&)t5vxJl60`JknT#%-iwqRxww@=NbrlS^5M7T3R`6 zZ>0c%UYGw}3j&RRI&XrcZxNA!mdL3gpx0vm|A+rCKgh-C-Y#20%&!??@^0ZT=g&YL zi=7CoDc^bD+`XA;U7UFRsC@7NLvnW`a$a4?a92vbVz%rECj6rTLJ=7+)WJfh}R;DOHr12PVx8JrxeZz zGtO6=11p{^2I12y`?`CNu9%6GsEJ%L(bizrCQdbp3<&S5zB(lgBD(J`y>A!=Dg6hz zY8RKApt&{R-)HS$zZYzyhdG61)Tehl@?jg#@k)Z}WW?clqYn4C_5v+f`W$I){&qNh z)>&Alm!;8Pw?#m{1-)+XeVGO$%m(J)(z^AT_f0V7Cg?||&QUGrrp;}TF#F6FC6Khh z!v{IMwz>hzxe)>ULL_(SK^^!1%RaQmNX$1^^P`je+ujxabE4L8NjpD%lDDzj_9klX zPmF*FW-g|Q%p~O3CuX#s=a)omIjM7 z6eREFMW&RLw+x0HMA%rZMGa=ZFL89frIP9V%c>yj{CkPhCLe#)w!F(Y=MHMmL;Cy3 zL8x}=#Fo#`W#r?ZjiGXEG2OocG701`-*@Be*X~iv8O26e8%&Zt1y3>IoR7>cy}^g)4%`F4EdBXI@Y1U zR!byf|F0G(OWxIFp ztgTP9^Bw%9E*s@u%tOV1M$-xEYD${=lkqgPAMHO~WL&iHd4$H-fh63A4 z7~e0L*#r~}YLUc@`V3GD})a-*|2iil5O zQvIQdiXT5w)#tkm_a9RCdFs&`8qz5%Q9aHd^^R!`Y!pnP>kwXkR1Y%}F0g}% zWJK*nse{7`ul)X5+5h@y>mEf$-0_o!jGkgoen^n9{`=u33Gw?R^1NGt$2duBrM{uz z)um8qaPr4n9S2`s#__D+wQhx;?w|dw4~LvMKUd!;ms~wPyS^hn*N(8f{*eZWutC~e z6%|3-dTw7!bO&N2Ra7R9&*bpiuqHJy=1{%nZ3Hsm>8as@-}j$AJsM7Aa`~eW^cnFc z7irV-iTCle+UtuXoxk88f@)G;Y`)i1C_ocAhh7KTnwF1%fsx1BQ^$GGErR>+tS1uk zP*6Z0dxRbtz!E3jTqayz{C{CCZ}R$9}z*6mygt(4M=p^1KvENY zV9?&cHh9hN;gvlDi!#9wrMBFj)+YvnIeE$SWK-WLpRvYitdzJkm~;u$?~o{2U9x1R`V@{F2yf0&{hTA~7XG||5+)B_#D^-9cZ+M~k2ZKr-dbty5WBBe_z8YBq5Y zbO78A)?zl}JMNrZGN?mX;(`4}B3!i8;PFk!mwhYyi|yf8mxPrte_|QoFTIwv`s6Ai zExN?H6#C)}@IsmS*IPgMfNQqH4<1SyTlBKzg1Z+;91i94b^JzTLQ2A)iE_PC$;>P! zD%k9TGY0Nj*_?a7J7d`H$|A3i8X9WVrA(&MZvMq5#rTQ;{_vqhj}?K;(5+A-UT}E; zbpw@6jwsQs%Fn9*D&BVcJc8@#RC#!MxPFAsZA+5#Da6Nt;1h~8A+?3W(-29mWLW0G z4V;dxq|PUUwwwc=%!gccH4EDvkr7<7>-NO=gud#M2%fhI8JvYEz(iFwdQ#|$=ouo9 zg^s4k3SKDq4rZA}&(0W;e_vmrb|#%VyFcSiHkCbVr-i=+S+*-W{zj5mi-y!W^f+wM zo9m7YAl{o49Z5_C8b`h>z7#qOLg%uQ5My2U=pK0gIFw_0_`+g1rKhXp#rvL~jzO}u zY+X6wtHctg#ab;2VY{&-c(!qwJ#^tpGW=`s@z3YA7FXt<5TjPMzhN)9x|hZ7S?W_0 zMSo-BKAMQSSloI?5wUa{!bF74-J`R$ND5dUkG|);g1D|d>YYgha?(fvqh^wfdPleE zOtLAa&a)8wc0;aJtA$YP(N;Ru{Dmkqk%Y%#TU)rDlW8j>NfRl2>nYt2h;hwKCHo9h zpNPyMNoP=fFqgqk+=G(iQ|<-pAlDpIa*H&JGFtcyR$0&fK%otX4Wip+63$Y7Oix=O z5pjIFH-$YuD_mONdY7YtY5ek%sX^gn{-Fn{c-4FIJlUY0f>Z6!lN0Xl4!KmkBytWP zhko=#Bc)%KNy5AV3=c95S$+ldIl_1Tzcn}AM$F}Vl=N$$C43#yDit=6?Lnl}fo>U5 zb_=w#7$|FxvC^ZBPOWlG5c&y!ck=Lkp<`O2&+UZ;`Pjm(V3jdGy(51`b0Cox69atS zloInwFk8Xss>Y=F>m6y^y5aB%`Jj(_kuwfi$}6P|!cz2iFm63#=L7{~_rR%dTVS*; zlOX%tD>Z+wtP)h=J>^7eHO;`7PIR|0i<$UTNZ{35DB1HY_Aij)B9iY1*^$A&HLVA; zb5rl;?AA-pXSMZ~&_$|>4X5qblm7U|@*};92;Bm0B!b5(r~K;DDy zrA&hyj>DT9MP%$uZ>>CJ)-Ro~#Lh7FQf6x9u{qrLZ&K$n5wLkH^O7p5Fr^#WNw=Eo z0b~}`Iw;Q(m`t_6-QQ*(OMRux8X}`WMS4uqX6&?ozomVUC`Okohq;B#8S)yuZd@r+Ki zFVB$_zgT*P#M*$mIu8>^z67kO7V7R-q~0T`GZrU6o|o-p@IKt(28I|N!4+jWQ)+Bz zcod18i7XaSqdDbHsmvSQAcVCq;tnugS4y*s89IytC5X_^A>Y2@)p>{zAsWuSbO?>G zq60hr;t7h6iv?qpgYVq^swpNiu@p9mF{2^4K(c z-~YWE49um^de-lrej%wHcyFgkRgW`aqU3w@OnH6U7sEVcqSdVWmd`D_q~Q;Qt^YXh znuV*w?<37@$BV(6lG0|>(Z81Utgt{^>nEQw=blwkQL$C@{DAohy~$s2q^Yu`mcfPa zn3s*uilP0ZPDJWBlteRd6E4-<5zRZ~RnsULu$P^MW!4t5iXI-74~^U!22ORUYha|0 z)!~-C69HwD4*xw|{JDY;nJp4}?@pko|MbwVXa<>-Hbt)LH#KrIH|oloQ2C~s1KYIF}uB;-@RV=aQT`uKP0jHxtOL;dXg)KK^OvEeR{l%%xsSPj^E_P9N6=Jjm=}HE*F3f; zeidnx@@ED}hzSVg1CehILVrg;a+PYXg=jQhc_ED_D?&guu~s1K8Cmq4%V z+uPeI+WC%MsT<#P4fEu=G=aAnbKi)g{~*74&b4o>c?}%qD0Jj=7)!o%{y3}kN|~at z5R8EE$1b0SAR-0$8&^XVCSHoi7VdgE6m`9vIo=QW+}@}Ym2ux|N{k6Iw9Zx6p6Hh1vx3#yIv#bVIzt{q3g;W<4OsP&u zE@f2e#G*B0R5&)a}O?dKaZrIQCR(#0gJ z^(m{Ep!x~10v$}_9IwcBfr|+(UF9G5r z(*QrnZ1C{X_o_iyij>9hW(nP&EtV2T z%FD?DqWTXbT;fAY_upD32Ecs`@ID6kq4gAh$+}~Z^*o}r;t?R??;i(*>bDx4_)i^pl zyU*rl<8H!F$oFP(t%0s;EM?47L&Hc5x4aF}byzKvM#CUt$9poiHbY;`*F0aF#|_Mz ztKc47Ph#&2YVEV~E>T;U4)$%exQ!d7J9*ToTHDt0{^AaJmkg%Y%Bw}E(sbFmR5XmW zR)g(_DryPfo7$|=}ffMiu?O!wt42=gG=Xy z$t4O^3X4o9?1y}djuwk3^TieGT&=X{Mhie?_hjL}t;#dYYfKAoi^|(LZBzVn%n0GC zpQAp6TvVm=n`DkdZSBzO0wWbyMMoQ()mxyT$j)_5Q}>H+H`mVACNuXMqPF+ z@Y{$_EV#%CC}MnBk7@b^tAxq%RkcG#p2o_0ODm?MT~GfV=G=HI=w~1Jv>Ozk;|mUU*xGecb&jrwu3c!|d-p zaioiH9s+_ztyhUeatQAnqH?(a8s%ovm-)yM5^qU>3mOWD*rSdvupVm@AP05a^qcX? zM?809=_5on`O4+%1Xcxx9w|6wF-d;PYbd*9(jZ~5EQT95f~5tNWJH+b3K>HlyE?t67P;P@v zhiHf%{b9mNhFwZiv^(Y`Ku%JtsQIa-AI!Gunn3z=SO9cVbH4IwG@@Utv3)B_c%YVt zC@|B$`fvkeY%fdY{F_LbX~>@$-fLhc1`R9(Gx=EC8Hi|22tX1w@OXcwbLKUB%So6036a|5rTTE+HyoYi*%!)%7)5Eh}KU!p4i#3!u8P}`ekV8ph zT5F%}`d98XJxbx-ZE{t`FNx<^^?*R|`p9dftid z+Edr?L782Hi6hSvTs|L{jwdI;&gA>dYwmFIDwNRO0{G^QUh9E8?(MT*l#hyK)gFd3 z?aHjPapBqt&PI@5%!7w?UBZwsV|p;hL>Z&lTJ#)qgS2L+6$msvt5my6|2u}bIRLtQw;UNK%c2mtmc=+u zLs8(@zlKmShtnlcp)zpa{giKttQq4KQnK~bLrPl0+p`AtJWRJt><2XNq@`?uW0$3k z%&Jl;<0s=f7edW*LaB*R0hGbj36RbXqkw=*%n!3S4U@zRy~1(XO`iU2{xXzJ?O1YJ1N> zaw1ep+SbKf`VexR`zi0CK@WTl*w{bROXb-98;Zc;7mfV${47V}QDIkcJB(RgF><5s zO&F{EpXwT4^MkR1Z017D8?OtHgCi4tB^OT6mnkD4 zkfe1wcR@AI-lt8lyYx6a%uBsKu$+5#v}(0GNJC|XEyddoH~P&br89pp6Cv-sIg%{& zWF*ea9cTw@6Rgy9p0VXGFn6&ZUMAfgkM1mc{=|_61$lF)3?9Zy&+iYPO zDos&bk@cq$9N(+197a@3azoGJ_+xE!8wb9%}K4DRqUGCtv2#$YN zq{+Kkt8RiqxPwWJW_)L`4cNzv`11TpEUMAfy~&iNZ!_aI2)x5<+=QvZqB!o1i?2nV zQt8n?IJ*Jjn609Uv*8F?OnaDhZhH@Hi`rHOR_qzw7DN^bz zjMU`^z3y_0P!=P-P0T;)t+QxC1iF9%|IXsosw+B>w&%DB^4YmB9fUNio^i%BZX7~Y zP{GDxWFX;hc@fHbMdIlnTllEF0aOtLz>xE|&|p{0xHzk{5eUR8TQlA9DUKB+JXi{k zAqIhp{(Gh3?o#;HWpiTi&Co7EW?yqa?>n)Q2wkH7UAxe^A0Wp<2?$900Xwd78-WYQ zLGo+~eJgsiJ@l`sZnI4beZJ;+Tk~_-@shVe&VSd2J%B-z_!)?95RqMQf9E9nPsy8E z@@_d#v8yRlta{R0lK#z48-aSXQP$D{t~0(sP+6tn&aD}HJ>krjfa2!HNTfuooVNk2 zg@80y14^n|(&eeI`2<(q#(lT8rI8Rd#^{{YRLxO}S9cFSakB5r3B^9_er;twb765E zJjVD}pdZN)v_)fi#j9Gk@AZF5#kFn=;mB_M)wh3mx|6S?|Aa?e>yfhwOW(oJIib4p zi&_#KSuZ${qC(GLoguX}T zLP--_FVc#C{jA24+g=6cu_vgKa*TUE&ix$zKqh~5<1p~M@HGw7_shfB&*5njK+?$! zkW6p%(rTC9qu}LBfd$I3y0D;A95|}x z>V#U00p5e$&*C-KnqbuaK7Zr2dF^aWkJw~c7DZl>?1C>Vo_foM70I@JmIPpv_k((o zBXTaK*k9xGQ0~CVoh{>m54@Y}GzZS(usfh;D>1kdz1t~8`X&h)RyAQrGux>-Ml*P~ z-saA8=bw#L*dU+S{JoKJPV;g4Z?~_%Yqc)s5eLw8pIDWskN6AZY}l$Ek*M~m5XU{~ zmVWW-F-legCL-%!AAJa4?WI+)-ns)SsaIRtWUlC(jZ_Jog;&{xWGT3>T2Qh>?xCU%~f19h0Y#M2B` zNHEH|N()w=Er0i59dBZKQQ5=xQQS2B3vt|uYZf!;YusA73>+%1W_;RE4M0lCfCoJ+ z-xpCZVKf3rXdt4_P`PpL&kYh!xg)!zg1ob|s^^(IcK|twg$RV`eP^D?I-mG4^=z-QeIbkcgt4CTYyi+ABwDTQu44 z(ErhtgnFhJB@7}U*N%@uu=>Oor!3xX?=RF{Rs@|S08|JvOT-mEhi1INmH&B!F00t~ zLW0MPJy~8F!tRWzPo&JeVa$oBmzEkAH>AHZx$j8Q!Sg#6HoJ7@irFU+wB{eL-?_DY zif9XE)0!wMmTmfnacMSc{oRKo-h0mU8zlHo*7THrh<7-QEiEmN`p(%YKpsqJt*%B_ zplSeOjnA7EQTUDhS-2#F9fl&K` z^E98qpNIYiTsMw7th~l8rieWn8aS$Y&7S^o$YNjNDCj~RLPoD7tNUCnE5gm)f8h&Y z_CcmT;I$23JQ0xmbQ=_MGy4ueF2SH)25t|Fn76@U26-F4=D}CK(;!fLhSuZxfi)4l zW@~bzD~}}44N(3NH=7YlUtnI-wf$#8e17?_djP?+8YQAf+xh5Lhh8J8^H(#$g#_pN zu}3P_4W!|}jC)4-{RCj!j#I7_DbIz=C}jk--#5nme5E65GXD%`5Mj=4BWjD{RIX`i9BqAd+6Js zo&NLbZuO*ZctR5|ur(7H^_T*WgpKaJ&URVY5>%Tw3Q=g^fTp9@d>%Kr0wkrQM=kcT zK0mhg9P}Fhdzwy_w{giep?MKEH#TA$rAKR>LJd+q1t_l>A%E2yWo@|bx|-1slI^!zUo`Y(PC=*!ykHBW~P$kLD~QTNs1&fNo!EWWt%Hz6q_IAR}e zq^0F&$D*pY1P-Gc+L;#=5X3tq5B*)cTIz)PG;Lc9gj(eyftalEo@VPQ!O zr8graq9md|gJU^?X5{oj|I1(5;&(Cs(km~6kWm#bYzuBpjjr_&OxJrU?^X-~(1M!CDXR^=tz$z*rEzAYM=POK zl;hF9xF+K*zfJYoiAh_Vnux&p(vYi}2arVs{j#8$qyfhE(+o5xBM}_6$s{wiis}Ka z7PD_+SH6N{!xU|jI1LT=&)Y0&=8iYPNAr(Jl3Ah)d^iu~!joa%e^|@^7=LWV*3AK4 zqPZsRs?98q5 z&Ge4|gTzYd$%4liIE$v`(?we+aiV*_&e#dP%at2BD6PD6Eh{8cV}Bd~W1OP%{f?^iNLX9&*-agpY!o-ElIBa}A%eAHao1D^b=N;Bu@{pyyX)t%o6VKauW zIn_!OLt%U=+yx;>OwqyRQb-aWreIjpQ$p5!Y+>erIr;#Z<3UmrU>6%QZk7T&Q06Fcix`PAr| ze04Dn%Ip*h-AZK8{$dT|vn!Y}WYj5wk^w80+U15Q#fcf6m>luO+EiN!j4VJSjzbhw zP1y*g?zx$bwGd+${E_v`d9DNRZve`j5dUz%%jOek*QlR9dwA-SrD8<>l=aj3gt4X` zt+F0iLibctZS7c`U0w`Lp0?}w3TEJ0UZK9=v21FQ85_0-gQ_4)L%e8%(~6&jv2HC& z$K$TyXF8o1w71PdsQ5jdvYsJbI?*ZiS*J;IYsv=~u{D|k<}<7M>=NKSfyAgv$w5ui zAGDBD){IM0pQjr3Z*;YF+ZgfZ=J*M0`H26QUd!;#OjQ|OIW9TH+u#PO5JXBvN!ow zVl|+L*@H6da;J6ds!tSM_;3?f7A>(lS@9**f^?8 zD_)j(fVZ1)59RygiQ2A{wxuwyjNBNa;518Xtr6)=avGa*&g2>2*%h4-#ARH#sE~Z4 zve~?dFZ`zn>=-b0j{9CC*rK8E#T66AQ*1ps%fk9}-ohzs+*q1w`<}0Pcz8I2@`HP@ z9;-kprh#g#3CZP`A&obkc+T{s=Db~{4 zEr=z9>z4M6s~}`XNt`J&;sRFUOds1H(9SCc#CJlIBYK9P&T8ji$ZE;6Ug?BqCl57J znCuOU8;{_0CVU1_TqH&%HX?oo51=`92rxiz7>hPnXLA|5_ggrX7Lch4-j^n<<7$i| z0gXsSkF!)fdl9TuUhbftPl{r_H}nC16<=FkY&s?Yg^e_5%t1CR&WE;GKR6KXGie$L08 zx6K~6TGS9C#k-z_qZ#jzj!T$5pJAhnIXrC%V9|QGSX{9GMC{FZx{Jeac=F2<8(I4H zqMrNwgwQO-mW2wuose_dh+HOB^p-*Bf@M|otc^TGSz%l}^$oM3A+L-C<91*_D zU+fk_!r!RJD$Oe6@KaTrpUG@es+Aa{3BMJ&%yea}Gdo4$V)z> z`n7&uw#vPN)NHB+CEiW7r0?Irhwt-yB96~+lVUgriPTPFLlPbnK`~<_C(u}+0bYn@ z#gak@ZN!&$_j*AyArGK#1y#`g+hr)fSbg$z;+E=5dYUy^t*7^5*VOAD$J=h-fAP2= zMfSR!{_waLJGKCzG>RMhImF^`4E*nPL$G6l;fFcLoxw~*&OQ&%=}DNqqSx6D!+oh5 zGGcoo8X$BN4ASNw>)u*d-a&%9d^$}mpPx-tTy;vmZ0&@~y7z1HyEHp%Pl!jNgY;y_ zLaJyXD8nnPc$Nfe7eD|aH8Bl;*Zu<#6a0yuED6#G9I&i9#I6@f?;T(L$6)AR3a%+vZE9`e3ZeZv*1O}C?#JS!F_$6WC zVe$+bkk{kfGQ3Ei( z_*Mz&sYD|&!sG(Ian%D0ypiuj6|2%O%qym{iJWgl0^aLCXsgPg|HDkfxzJK+p%pi*V^?9_%>+C9g%8vj88JHJ#LKq{!l4z<2apA zxNALxelwQ$vZa?g=A1bL5%tauStv1cv-XxLT>3HNXEy?G{$2NbpD^}Mnxb3X7C#A- z5A)mU1fnA=m#bUoU*l~TyI*09+zwX0lQS=P`uku+4z)I^dia0M<4Inou1=AjPx=xF zgsMZG85h_q81oCz!n@GUq^kFqPj>P(p=q2AUyhCu*&j*&0K_ueQ8fJ*AkDn^AODOm z#y+Ok9X*WiTLGx1Ca&U5Mw7WnckAH>&`uXO8$IE#=wIv*Y|2F&vvR}~1LB&`WF}NW zNOC#VplN+8iI>3&`R5wShp|gvxcVRlGiGERf*kNp?k{7}Qmd((Z#5f<&Vy)`|2hwf zbnTUB?#0Y|vb~nF8t&dk#lNDJx(i5&BLEMJtWJXstT;~604x&>>SxZ-=h;45iGBQ2 zFb5k#tBfKjJEnZlS$PzqyB9w2F?a6O;WzC?gWfGhbJbL({y#MHMjVJb6-<7=X zjnge3#8=-8!Mh|s%J0wHYAwut-x`tnT*X}K;?=Zn0`lw3x^7Ukfw_lICodSISyDd7 znXC$ksrj!ul}@~Krj?*`+JTIE?-fsf$V~*o{@P%F*RZ{BrE~sofNHF{R z!4FIoOnl3^FgkjQ#d#k~x5n5I8f{nn4vDz)Cd@A2x3Vk(>-*hp^c4V|TgiJIc_F{& zBBzQt_`MmUBZE!RWf5VK%94tyC%G+wipygmrj2Gz6a+A5E@30dunjlbS|A?aaohWH z>JtR@vgP{XtsOc6wq^#~WB8oGUcedA@~-97iYNWo2tU9;ciF|TDghXRnpJdi$qRX; z^KsrZr;JnW>=rSB5^){e48f#ndqmK5pY?$}uPy%lVc)`TiptBo{KOhkr@b6@27C7X zWgCYBG!y=eNJH6-e=~w1*bjwW2cg~U&GS*Xb0unBA@bXQtNRS+2$TCJTNxF`Iekq0 za?gFLarc+UQl*bxZW~SmruWHBG%G^;u-L9|_WH5K$=_1-jo?xxp&(=SAh5HjgNy;?hgHOF!VcG3E`b04d*0YUYJqO>i12~>k03yqZZID+nhqI6tn2IKwA zad&&{CKhFlcEGU!%@Z3TTkDiW(Y=4<$#U4)rt#xtC2wUt(D>9`{|Q-)6;Mej(m^e~ z1wg_3?;_Uj4v+id`)hZ`W(C}>)~E|!R6@|))m#D_fZLU-@O9Xk>HT%ne-AE*=HgJGI*-F|o$>pw`aI^9oJdg%LwRAYZrT^p0R~0Kc{4C%_~vDQtEE6hd2G#FuBh(7h2gu88Zei^Dy}#-KbA-TlvMrgaCgS zsnl@pj%p>9HWmsj-~1aHalGx6*0luBm57k|^NkmcZaGSJS8@1jWcxL9&L`V{u8zWt z?oG*u0wt&=pURbnX7bHE?t8&3rzw0J0O^Qlohik(0ig=>gZ6bV^`Nu= zUb*7`I6~KbwX?9d9}tko&?n+L?&T`_3vb?`0a7)EYO9a^h)7kNex2Sx?(Yk=6@JU@tdn@s#V z&V|c3Z-Yw>SnMd;I&UK|kN;tfHiNss1Z;7#KLg>yu@3?6a|vt-O(UGG?SVnSND-j9Ne`Q5 zzYV^TdvnSEwHElle^?V|JkeT>1%`!GyROF!PeQLK5*FebUDqUMl&GKSt3v4wzme{T=pEayL<-}n;KiR#^&8Gcz^PZkE{ zF`TLjXGma_5;3mf@8TU%YDq#H)JTRfRqA^RqvDL4}{6L3VhyD z^?J(v(WRdQc1EMIwVe}Fk+1G8U+nfA;XN;?ea$xlM{odgHg^yclcyul90bUfbw7w- z2@8Iu)q7CiC9IibH_M36+Tp58?t@_Mlf{IYP*+Dg%QTW^f|%s^rDM`nrD)x+>qXNtSft;$h}jIWD06zNCKfuqtI&8TtC z6ruEYh5bf+t1DS0iH3Do@o6ylxQcCAz>q7A;K%}?SvtxAnkB{P_C|O+?^%$By$LWb z>xl?m%=Zd2G|Vx9iN7RGv$o>86VWLbq>#U!5D2trWW`n#6{&l4>SXB@8bV@!iiPA4 zy532`HKQO$DAE-tac3o9#T0n5kfUesphcX5vfdCKyk}Nc1MnkkIHHTLj;h2a=z)^C zo2Rf-(flkd{?FSIr>qh^g`P;kgdwACQ2^3vP4vfFm)-ISXNuzmc4N|F625%i@|L{y zA_x1-P6Vq~l?$Jsq2U0tb_kl^U~IG%f(GMLbmDDB5ij~{9ub)P_Ww0*Bc2GNf6QQu zQ|b!A6&1@#9O1cOV`oG$#sh<%E!? zD?;*^*B+ODOqh^(O78%X!uyyAp*P@Pb9!davp9^`4^Gc&3I{YX;$i}bcB}n3sXJ+V zCa0~-k^22UP$8|D~_OcwEx9H06qjO4MIP7J_f+O5O}+4y;@ zYzo9>q+GDp4B`>0`@3wg}J_*@IPwf~-Zfy(V_1>YN4^ zH`jqmqerV3R}Mk*ZlEW8*fM_w%bxKXsd$qFnub14dYf}+^zcBAJ*VaI%+ks}_$3Um zJxmuCdIN5N(~w460+oTjNV?Q#8>pJ_3`$^om43Fe!(NDfjV|@wCP*N;0s8v8y_bS zru03i>f0LjGrsjiSb!kf758~Cz;&y1@q#e*iW<-v!Lh3;_J_>H1L{XjzvvoWD}%k~ zt^~Xd+8W=*FQ@}({>mzB*$N7EsXbCB!JfjI8bs;h>as}(mfxo`Z1)QPj|$5l%5)I7yI<(;hPayOPZn(<*!s{7lI7CONUZXmFrcx_ylQ)ag(B0qz-&)XhwR|wFFwECcJx7cO%bmaS-?Pbkg0lh(;acx?$&DqUTGSM_)7rF-w${B*j zSTePClVimn>lISc&Kne&PEoG@!%kWD9#ElIJXtlV5z0;5;khdHDoyeL0$4w;rN2WD z>>$2eqVX$Z5?$*|7uy0(9UJXw+ELJ)|Dd+&jqEHw)_q&^(jyhFIr{<-RjE?$mZ&mHDJQ^agAEfO-c2rmMcV3$0^`^?g#dg_C$(!d z1SMMuSm^@J?C|?8hQON%{`b*;>nq|5ACH#Jcrk$+AoiXO(rL#K0Fu|d;ePOcSG=t; z((n(!7#iq83sCDWExgDY7ySM*;RL|vCBpKH{LrSCm6tb#$rJyh3%yDoG+h-kt-w5P z?Lf0g5)&l;$J?cDdDH46>bNw7yG84@TlU>~rjwcl^(1m3PnrIdCb_SCdxOrF*Nz61 z#0E)v=a)Z@zoqJM)(x)wQm3Ad(G<9^vZ&pRQ|_afhw?4wdDYQ}5mBKBay3{KIJPaP zYIe|PO|opAB9lGg_N?6IE9mSSx(g*>@ZIP5oUATf6mVcoj*riv-JTeZ;R&55pGu+M zPl8(28d<~iRU^d!KW23$;E9)JGVB4&XKAn*UqI|T zZ$VWT?RFtYRP{b%@A9FOwrO&ERsVdNyGnW>i=+v%DRs9;R68eJ)(81<5sN|ZyoWfm@NR`-8j3vf$SE1z&K!To%_7wT00)@m zqb=0l`MIf-hf&%Z9^WLBwh;An$eKy+k zuI_SW8Ta=9%YQ|^SPoY4$8Q8m<%{2q<+=dl&li!JiEo)bL*5z;N~D<~D=KJfgzwyp zOz$hDQyycVojfhT)<`^3-X2aMY0qX=xeJRT@AlCB%kIHm6BBv&tAlky!Q(^MnFN~c z=sN=yoe!QsePNcq?UYmsi8{hQBwH5ySF_pNCbtg=tsRqFL31KV6&Nc{J|dga&Ocdo8UHw=5P|)1K-UDwgs& zKMYR9gi{+qR`?jx_U~^wxZsjKug0%l8+ihWXHUYI$M?O<_r)5*z~tc=!Y1%-~vCj##jI;Ifcw$y zcL9AAt7CLBJq9XX9fmC3Pzxw5Gr)jkGhw>4Y2i&!({Dw?QK5%r?@SzyFA?q)eS8P` z$h35ACVPLg;I!{gGjIz)W6f8Zu@hb0HM4#6O4=8ED_`*&ghAWIZ4uxe&N}s@cINAY zAVlWwj6TDM1f5n|Uws>xRD$K4Iq7fd#>cNf%+7`>Ba}0>-YRDw+num3B^4oqzH9x= zyfFH!L5++7gom{w;3SV7IZj3C*vgVv3=I@NVnJHia~jjbwaAesP`p%OUcMij=68BM zjT~RrtNW5lJCE~4O;wX=1hQJFcb~g2{l@(%3 zRZNS9C^Y;7VIVR0mr#H(%5OXF$QlT!c(r5UJmh}kF6)96%uWTdDb>io>i!AsZSo4I zcllrOqWt>&aUu3>0pf8jb~F6UwsTrZaFgqmn9hrt(JU>2UgaoYfL6-xpJ*ff!&%+hlFi=k zrG`%M^Phv)p*h_<`$xU?LK%<2pb=7hKz}cqbcyqH zZEhtDY_sVWw&1u*tbLnOn=b*e1|Tpca9l0yWQ0zEQpFufH4^J6KIT$tI4#6W+&dBc zdq6wo6UXF&l{957!6w$eIV!C2yFxJfs1JgSLiLkRyvWGe4EnP!e$p~5Kz4>~^Ud2W zdR9kjHvXx#DSq!6>g5tY6l$*6I0XGE(L_|lpk2{&?(O?g>s*QDK8ij>jyHLXE7>z_ z%foW3o_tm@=k1bWyPxK8xm*;=c;(kKCix872)*Hy$;rVt;Cv0bNnWER8=f~qpP){2 zCY!;uvc;Pdht3Nh0f6Sqs_TU*faMzibl0-dd~w`gnZFO5m+{*yg+TUj`@~Fu*hx~l zvd~ZOfz1nYJjDWg??hs;6HXDSy4%Ik36R?*Gf53Tdmk;+Kk(Z-JbdYMW~)-2Kc0IE z#=-4Jl5UU)+_K}0I6)%NoKJiVk1lt6=<32(Ws<)0854WFyW1 z?t6@e>Ut0~23`X1VVk@Iw8H||{(@3cF(@F5MM40&GVLFuH7opzrx8fimZ-HAm|VG$ zDE1)mvW(2d+ff1T4a-X`2IpSGoqc8?0C^EM6X{(2s=o8R~{4kM;| ze!LQ;(cSQUIVlQRid+RD+yX3Q3@CbtsYY;(76h`j@C!Tn{l#8$n3uz5&=$3{iM2(c znxiPXZMmeq`ED95`E10_jv81tP@IL_o~5_nJvOhgmJd`4w#Ez7pp6Du#4$t3mNxuS zTM(-QR$k=6HTfZ2Ot`7(`4+`jcDtRH-aQ`d!325S*XjCDvQ;)~->Xw5Pa`H*k6Gp9 z()SRhGn4T~n)4@TXBgS<$8+-$XFD4gj6P=h8rq!I>Q@CdF3d=toYF6kYG6)eA>zq%rsPTzrgY zLZYm1n6&z(C=C+Dn63)~jj~*{Q#hR1urjznHH}DkIriyjbjMV!6lSyR9O)tP;REii zzoOyX3h`K302@(lKQ8*f>JC)(tH@IJf7&Xd4mSIT$y?91gOqWYZp{8hg78FGO5n4t z-rhjN)<@U0N%Z*nW2O$~pWNvfnGOU=%9DKueq!~n3A?54lznb z!x?EtWJH6bTp!VMXKeg{6A}qUGsgsN_pvV@-w}L>@*ics-WV^%K0=V6inJ<#KLh(e z=RM^fEdpAxz2a$;s*Yx@>PMM=ge(9o34$H~D^BNaf31-}q*7|ze zi<@Ql2=F?^(u8j|UM7QDIYwzg{;CjEVbgN8-Wi@Kjj7a5t$7VPMnI86(g%NcuisUs z62+ec!jvHNY0l#Lb3Fz4%l+dXxrnm~9@@mENwZnj0nDb`{lo?LwL7(S_9-|p^F%7K zg)%uM%qcSZ4|zD#5X_x#x0{NF_sJVvKo=j~wXr=qmj5mII7eS;KCB)31O4u@Cu_#T zxv#amB*9jeu>bmRUI4BYiagUZ^;StRJ(mr2KC5Zv)<&V^jB|s0!gdPTZ1F@oxFUG6 zy(sqE&miK|#lR{7Zzi}X%lp}o4ibQV9!n`hb?hS)JL(}?gb+?*m z<#FR>=0?mwCZKWi910D;(EQ%(L$hg_(chzA9IlK{v*2<5fUarjqRfTS0dDx(z#t2O zQjow$abQWCO5u)z0?<#>|3O#nKS|&JVhPMxgT~(-)?lgJ1V+Sl>s%0?M`(_Lss94L CV+!2> literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_nft/00002.png b/tests/ragger/snapshots/stax/test_nft/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..5c983c9d1ce0eeeb23207717fb8f4441509d39fa GIT binary patch literal 17882 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 NhN||Xst4BL{}&d(HTeJl literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_nft/00003.png b/tests/ragger/snapshots/stax/test_nft/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..2a281fb07a778e19619b4fd0a5d9cbec264d08c1 GIT binary patch literal 10580 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 z zfxctKA5#<7cpOGT1dwWOgs)67tvuuPu*b!9Ljn8Q^xQnnihW&8b?loDl>clK#A!67 ztE4Wzu&h2S-t1DF%=}jObD9}d=s;V0>;I0%7b-Gfnc7!^$=pqFy!ChgBG;G$W8&fV z2f@*@{i=p;NE=Cj6`=YUO;sJP&f(5Zm(@X_n0@UhwVphQ2)Hy@4Bf~V+(9xfy!p($ z%1;a}Y`b`4H6zv=OkZefl zoKl1XA)+SRDQ4~yMMFHZS-S@gcdbI!`i@1s`8XX~cx#-rM4_`$E~OjMO~f?wP`g*q z@Bb(ycvXoaG>60j`_e3Y`_V+H=!AIm*m*I_%V*})%?nv@r(uO&H-Pf!Uo98~c&*qw z)j5DM2tc25qkzCc0p%tS^-XIz#Ki@_y_ym9Ve5g{T}E6-VKFV-H*t)uY{2A`=*qdq ztj(^y-_l66@Z(Q_zDVzBp(D|S zP3?br$+}n5NJhs)9~N-PsLm!kXN_?zD*%w} zTUsHYjpYY22JIROJwW!nN z`kzUPk^HHK-D&l$bfn9eniICy#)h^wop2ffWGeY4>n6JV+W;F$Vk~Ww6i=WxK6e64 zdBL4_1biNfV5yHA&<2Qq98TMk=BW2ClE?coAb$_R0~qU6zoBQls0SehRwerZ&N(jl z6ajfEr{P`4{=~^B;wQ$y4h3)Frw*(~Tzh%}Fqb1Yyq>YkGg;`Bg~Q2HQMJq+IQG;y zPoM;~cg``r_h+3hZC+tpv{Ms$I0F#U*4Ea0tC{sxmOMP%Y29)pvbJi$;|t|tT5A2p zBFezQ{B8I#{dnCRSR?^Z+UqcD1HEz z&p3Vups;i=awsfr&+lP2BbjMF()}{TnMWxyj{mjOK)4Rv1OCp+jB(q*bCiLE_jiloQLCb$3 zfw>m*90n6?a?AfX4|n?XUK?`d_hcHAEX4nb|EVODuHI+r?)2wh2=u%(O+JAJ_6jEsyfGVl_Tx4e+@hPJ|46U!vi z<}97H?e67(EhAliLrg80Y*(6Tprv12?IxbrpBOA}o^R=1JG1PFg#%cjDx=ZM32SO| zZ=F{iJfTU(*Pb%y9<}!j2aYiyJ?dupnnDSudjCn=c5h(Kxev=HvX?!M84I53Zk}BM zO7=&PjF0Rl3#mIRt4=K}HW=9n9^KNW!;%gP93#9=JgXMe^ZEJFFM-&Yv z>RgJ5YH{_hY=8QB#PhUy?mew|#MUzr1`jUMi9ZH3X>Cg1%b*|5-@c>ysd1Mopc|)L zucD1`c(GZxnCOX!M-a=La=7K`3LLIWgT7%|dit(<$b_d)Z-**_cZ|NfjQjC?KVXor z%}CU3kQkFgtmT@Nj_n7#V3d5a`BOnPW8FUY@$@5{kHpG-;CYW4#s$O5da2E|y7v+WK5=!8 z#mRO7Km&<~-a=rvty{H2^!-eWVFPh;?PAlIer0zegpyG#LFSY}-M2UWjy^xFdqLd1 z|Lke#ZOUxe#ol}mEmAMtCE4TXSis>_>ip{F=DZ-EdA|0f*dYp6J`3y|&lVUD zk1iy?BaiZmGLm|CXybpH`FXoEr+r-rXbaFc11N%l*mg#qR+23_d(-I*^kh>>EekBA z9{q?R-ByPL*1J3J!Nji#*Uuzh?09l>5JH7N z`Amd;1G8~T{(_&j=&qLZM^B0S6Fn!vjr@G;@(g*%%7RL{C`M_-?#%oc^%J-8t5W`a zT)R2BN?`As@ul`&=Wme30(!y@#t2 z5<5vsxF4-?(NEU&_DV;DG9ERZU}guNGB#*$fr?IM{rTVAD7bAby|m}*r|l*~pj#3I NHMP20b=foV{{UOlq~ZVo literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_nft/00004.png b/tests/ragger/snapshots/stax/test_nft/00004.png new file mode 100644 index 0000000000000000000000000000000000000000..67f63f9531670de57ff3dd921a66a9beba11a4a1 GIT binary patch literal 11722 zcmeHtS6EY9yDlOYq%K982o{7dtm|%=yeRGR9Zl?|r|Q_stBs&Iq4j zVPWAix_jpV3(K)rEG(=aPO}16G|XEoSy*IW8{N@;6!wlvn#x0&yiIftuaQ5-8Owc4 zuY*^CjZ+smh_jv6z5NO}uosb?Oo<}|vpam*2h)a))i$dRmC42X_lA`?^-i(y(_)t7 zSXemqosQjq-og6eWbBDQxmY=#3A6FDD4zzgc=6h>j9jW^DcAkq8~%44{_pKUp{-at zxi}ZgxAP|(f2lR!*SV+vIg!jJ!@${YV9|{37JQt!o|Cf+432*e!Xps*O$s{-!0m6v zU&8DyNBx=#>J)Y~+4vP#`?V#bx)^=H`Q$X?Xj!wLjqE%A?snSWax0UkNBO!LkW zgTLyoq1?00>kEW5dE~Xz%po?BlB^th=u%5dOE>VnD8%)wce4v#QJ?3YsudE>WuFsE zmArh?a~q9Mi`Tcp>dzTc0rjVbV3kbEuE}W&D1)XiQ?*<6LqoqWH!oz4N!;9@lVjxw6*qE` z)K$qj>U%yp@8PE)Ui(xaHsO|8*>E&Y)v|0%Y9X^_}M06*z~LvIZYi z!!}KFV1kKFQ?kj$8;f0qy1=Z6Q1i0k-AF&~Um~PqH0I%Mr1pEWhx~kEX&s#xLkzJL zjA#Bmj=?oTU{z6(o|hd;X`gmQB~v5->{XQ?LgbjYCRkX07e)8q6WTbo+Wr@wktQVwVY@{?(ID=XhK` z-8#7urj`xAY!Bfye8bE06k(^Uz_^nQqc5!B^v zkbK>e_#|2wqdeKE@L1%y!H2|z8(tTw%8K|1VvJDtvq8Yz`4WqS78OM11=l%QIp))v z?ay`JNYhOEC>GE>=2x4lCiRXWxg7y3$?jpw}fo*3Ey^z?~ z{mck(+CPt^lg9p7?>G%O{-AR2XDB4%oAzHqfCmrTrlF*$zu)AnsF?HofD>Y!%O9I? zZGuaWKo{(O=J|(BZte);F^2IwkaYQQ<-$`XOK!aA0j@h7NuP33%8&p5CwXmM58QBrxv` zgNtz|6-(zI{nDfbYn;%%AJL9LjC#2pwvzm6DD91Je?0HfONa>)a7qPiUQ-l%XqUa{ zUOz!&1c*!#ur(A+&7+;g4Lpa26`TytR!^VD`1#5z*dAGp28Qi#xn4CL7PH)Dwm}XR zI01KD37(`e+G$fq8t=`}3^>7KmJv0pQ%I=ii@sE!4k!0RvIzm9l?XM>!=1XKszl4| zk_tN*JXE_-i%{=)pYLl3HVa-w@)JlbIvtA8Y{5cUF_&z1>?td(A7P3%TUQvIb zvjS%#d{X7AxiNdSeE~W($NXnR6P@X}9DE5BiI-G7N}eB&&QV7#?v5RA+nUe!YqBH2 zQP_pPvUumPHV3L+?KZewv8Ghc(0-+|1GEW!v`PnnfHW z84@N(wF3}oo)_BrKN?rYp(KmcQFxTnY~>s2WS9Dh2JZPuzoz3l-oUV35s#vVFn#{G z<9FBT;%nnY5 zU;~wQzto`d^nE51dvpm#uG@(8%rUm+svMJ=-1|E8k$ipRFq0?llIbvhF;Kg3(R-S% zIjp&YaP9$iBxQalQoE2qp0u8Rg;a)pthn_(;Y0wIlrJVz5(pvM%PLzpueNJ~(?N5A zE#u0l$zuxP&{gBr2eg}C6{f}*QB2wSdpq=5MDl7CWy{N7m5hU~$J(LE>$N&JIJ@Y( zk!=q0ImXm{bX!A9dfH%#r7qa%Kq*mK4R`fI;_9!Ot3%ooUa(F2EFG~MH`((r!v83f zR^w`oCm!CPW!PnC1`QBhZU-lPUmm@|;RB}k5ks9CWQuV|A;2n-M}=+ zNK?AbTJS-TM0ZPL1J7zAWh-2(mE)uInIN3EwT>T=uF`@SkDM8~T1vIN!9iY6VY0WMumctm6GG+lyxb^y`6}n`9p>&B9Nl#B!kf1tJ&#Ui)2A-3 z)9FFlg$!m?(9ZI@pFY!O1(U0_aPKKWA?5N{L2}WpyUE4h>6%8FB)?F`;f$em_q&t& z7FAZ2)R0>)ywjw!lQ{yIkZcNO25HE#-Y3;NKh`5U6XIj?U{d0t3U}qT({Yzh|E*So zkinzoH=eLQsh{9%Nf_m>f(t~ziATz6c#=n`&ZESvygv-i$+fuSW<9`?QmLyP>>vXD zafb!bay#js+EWkHCx1!pwaX)YASwTuf5TX9zCAIkw3yU28k7-CIqPu0X5x`0+5xFr zU>Y=bk^Oa#&PHI%mjL^zR5x&M7SsLK!GT2NaR`(jQUcY!R+}oF+?TWa%BrX1XB`HD zo$xU^u50^4(Q2MdCWp6iuVh_t#?8+9HBq*Ny926&?ipaTz@0|6Fr*VV&w0lP%subn z5%GpoDcgnq+dD(p6f^@2Cs)fJp+L|R*)7+;1iVvAms*JyHIpOu&Z9v6Kdf40t{e$l z-;VI;p*}n((Du1zc`LG5eP}7|5v1gz?n5ph+LMrn+*?W8N$>ANlog?>7E{VOW1uj* z!*FOi zaRDWdLY%d=r&!6gT9NL$3&NBw42%atU8|4f;=&e2*H3&eF=**N2-6%3cbNHW*G`fu zq15z%K#lm=BXK*T6E%rjsEcCmHJ=?5EkzeYW|1H30&HLuj`^V7L;PoiS@E;`8=+2!P7we*kD zsM(=+D|2gBRCUdK`t&`V9hHA%66>z6Vvu8XA}SV&B_%3t=6h}h`5*G$%JRv)PvfjA zY_yZamgcP3cJ4;5O{&4k6UVehe{RxpmSD;?@x@07W8%a(&XUQKaX1-Hks1j69rMrh z=EtYcH`he8*qfC-B(D6CSj2fcJh(@(`_KGR3%#JqlKZ}X z5<=A~x_?}FT(7F<7%ev!-Zpl)Wy0>}kh~-TF~ZbHN4i(`-r=Y&ls*z}en;j4r!2k= zv9*m^!q?K+f*}m3wN_uSnZKyS`u_Q41mf}amYOLj_FKcMUj@9yInCqV6I|#HRgK^? z8c?`%MsLhTz2trTUPpKV5-AVs;6J`pj5OLg;|2yyX4Gp`*|xL&SBc;9ne`i|LEQoG zK={Y^%`?T(6HvDXgb-#(c}nM*>Ib8p7_WzZO*7LEFEnud)nD#>P&k?T*P=W<)qdzo z^)n%hu00;l^OHe0VQ?uuk!1|4yWwvhK=Sf`K@OPW@}J{6`UDo3+Klrb+y^=`jfNNN zwgVQHl$g)1B`saS-S^iS);K|$(hQHBeE1A1OO~V01mHj(N1O*Y7n-o9b&Kv9WPw|; zLS=UwWA>bD6iwW|zx%p8sIj=ws*lPbV9R=_A(ZgCws+V*?6-p=Y*vmC)I_*-c_SmwSw=r8tJH3uqM|?rq2Y@- zL7PCo?+au9d7uYAUZ%dqJp0xSrY0haxQ-T{sXY_-K}bY#woWrzU##%Gl`il1M}Peq z_Wji=^5$mr$NiR$PSy#CK8MNm*Wxc}Omlc;SEbaic_><{9yI0G^sAX-2sfygx&~mf z?a;@3`b(-@>D}|Gv0rOXI+c_BREw|Lb37#YRu4|$9B=I+*SVXjgtJWyC9)iTgDkf`^1k76js%1l$IYvr^3YJn(-U{93rNv{10xOtmYrkVtg{RtR};=+ z%nBxXjwS^H@@?f~f-m;#@{UCFZk7dP)l!}aqQHp)qlZ6$WH0sros!k1`pCEES7lwb z;{`3E9@gEa@*w-?V&DpRcq|S|eXGFf51^>zY0@22bY=Wf0A)~dUB>xHYQ|0ABaF9E zxLJM;b)lZ8Ex^{8z3Q+r%G;fE5WX2T+qzTKfg$-Fj3zA&G@7Tv+5bp-Jl5QSh`K)M zF|T(4(YTnbfYlhW=`08*kpIC!)ZrN@X)=PUMTX7nMrJ=pZyR@RPrfI3MEo5B%PGIOyxq?J z8#Gb2YzsEqwx|AHlFbZWVk8mBK~!t&B%k$IePuBjI|7vS8)CoNm-)5KHHy*r74#W> zFr@i6wVkJ8B-bUE#&SzcJ_q>X4R4wAUKUf=LOef8XA0OOts}}d#r+AAKZiJl>(BH% zW;;4LX<_8U$Gg{o>M>9jUiY>maMEjXn?I-Vr8D>JnCwiwPG70NXi%rNq?ed26WO-n zC1f3u1BnqCR}gvdh^c2s5zui}eRTA5b5)?e!~NiE@IiBp-K~kf{E+uL=-x0YF2uAt zgpX_D4kTaKHRir~p`N*u(LhLx-KQj0@9FmPinOa#4Zd0TYq6u2I;v#5`b;1K4=MSQ z`j=9@{ARJ&oI--EQMm^mVyJC1UoF;E4P%N+f#{hkcvsYyL=08~V%O7JnYX2*qeG;i zp`M|~@ggUyFWTu?MN~_{#O_rVrsuzg6xBz_z{OT(G&_Gk8o=xfX|Z2ze?2#gM%KS| z=7Jo6mQqnkee4x8jEwgYn|qZ}J&l!jhi596MK3RNZ7u&?Wkd*xFYa^aX4=y=%}<)7otDS; zQMUHhD0aAU$!U2w`MsG`o(=Fu_PAJD6j`uFijlB;+xEvJE*S$%fnL8hZzaMONi699 zb|}h=(wVXSaw6g=5o0eGAVOp<1{t?h17!p*{N3h-L}deu$3O2Sbvf5{%@$4wEIFp^ zk%sg~YWEmTyT~?@YC)RkwJ^BUsaNX93vOQLY8byTVGuEx__Jx2#$dL+6V$i({ylIP z;3W$B-GlXA@2d7jwAaYx;Jwg+(XCPKwG1FOa!aKlnD;T}z!l&}mgbb6CE~2@(Mahs3$*uPv@$+7)KG z@Dob|f%);(TFnD7Qld9z--ToqLG3bXil>;RWp}&h0h|@m=A`b>R3y3xn*qFR_7sHp z%XUIpk{Ol@0iyEXv|l&zg3a=Aj{cy@C}Va|%e%o~P0_{AdO0P9>fwE&_W({Ma)ui6 zn_N-@{ag_8m>f1~#{l+gf5%(c+BM5r2#ED+J#?Uwp@m>oPZ<(S^Xd7I9mlEN2{!kf z0OIb#=#{pw?hzAFZ0>e=^zLMF2o_>)U3@Sz_N8Ln>S84oPMG~5rqHnL$ z&w4&-I5Za3!K|!0TlEi`E1H+44uu^xeRqjK!lwmhXJ>&0M%&6({%VeYy|#4yk?3!n z{GvZ#eLLo(G{E$m4y&d2xA`yO(5hn?!ID!Lm4zmi$4b9J6_rEJ%=P;gNu5N!(R;j! zKq}_WyBj?Xc;`aR=oY4@XPR z)9H5JkW-zjP4nZ~XKk9Msw=+|!+9C@vY8h$BDi0>`tc^CHrnu(`oVPLdJlkuUi}p9( z-AEWSx*K$hxdgW%MNq8-6=%NLS97 z-^sf+7`H%)d}QeJ>ASmX>%2LL2RJv#NQXkue6ES4`udil%-@6a z(C>H8MeNS|B@}$2KX-vo+@Bx6=rL?ICfCHjq4%mQZAyScWTL*ggY}slFgex^$p4BZ z|0^2(_lsxqh4X6Q?0yI*OEV1^Itvc`9%lTPVEq4C%G*&5J;u`7OC%CW#QpvK)*|tT zmusI9ZipA{&7#)#uhg<^87z#D^kr|hoCf__$Nmuk?J-V~W#zcP0<{?3f4lgFKlX$) z9bWDg{r6dyln&OBu$V{6I#Dy@E+YINx>`Nm`s_HpY&wQy#Sy7Om9`_b&-CqVZH4Oj zQaf0)z3V(BW*jwHIc&-k%Cf?OF0+H2UOxJi73N_9i~{&dvcl|Qff0BF7@5}rBi*;9 zSz&r}?Egqp9S_QD99~)@Sz}LtI!^(Qtt9`eUq0K7GTFM<>IeN-((LHdt_qiMXn63i z<2sp%f!od^ZIu6AErkLe;4MAYum0K6E$c@>%pRRCVSy_iWZYkX$pPe8?)F!`w{F+U zN+=jOS!-{*Sb8Cr46C7-J@_O6lo;Lfg8}ajqQ$y5roYC{@5gR`*xB35|3#J=QaZ>R zSP51%ZfE{Y-}P|-l7!T?KLry}9SWR=t)%!zBHeG^dawh^S6jD#)6}#?CxO5k{dUt= za{9@BAF=+ICIQt4Bx-057#08_LLm<~_CMucJrv%ng=djrnE>y3*GKkYerY?6* zGVS*Hc18InP{y4Dv<`c@pd41bgy$BWUcU}k26lmyKZ_%T^z-~y6}z>S?r+LM+z8Mc z@IwJ2vyA7L)}xN{+?D|DwFT@12p-`fnc@Le94Sje-2rizp0pXL?_wT<-SdmvX%TP_ zxm)9Xz2R^oRM$#lz8AImn_I0nT`&S)!_y-{ibmtl3CSsHh*O11fhpzsKZV+H<2WNu!uusW|LA>R^mGS6 zye@&x{1%uTkwp9=YL?dTty0jh_pu4cbFelLJ+S2+!K32YY6y1DI0(Y{haQzqUJxSC z2XJ1ag`UL4FwOjsCxVHk+F7L)6=6ZQy01zre9nQ9m#2U&(Aa6w(g|(G!yF{pOKPz>o^tJzG=^eb;5VacoL~huxu(AC^3b=|4tCkmR zlY^MZC_BcqIV?KXP$ml+yz4h}s#Q)FR~jv=nenpmR4Y#od}Zg{f7_H*Mm}fg*F-!q zrsoywHqf(_cWIUWSrkJ$XHy!}%QzTwo7{?HKkmJW30{%^6e%5*PH*yT+v$awfgt+`;qL2HW&5nwil8&-rf{H#Bh z{z``4(ETx)CPCaueA_=GsnofuuDSAr{V?3w3C0Lc6R$mW zCK&R(iRLRhwB?E`^%ng6Mo+@F%>`oef=|EBwz_eO+N`@Bk{+Dp`(0(Fx7*sse0uZ^ zcyhQGwxR|aE*z2|(EHtoBWJWU%4pZ#c3VjIsidkha0X8HJfl$<| z10=aYbI4WEZ(05H*24z@L4OY^N!@+o%cwBE?$Kk-o)jVNvPY?lY?q zWGrM*>wpyVO6YGN%bg}E4CPv-|LjmCZ|jlmVy^Tle%%2(-k8ES6eKlT_x0>1L+fC006+tZ={kzp z;`XgwG~^qUi&y#F6&G6?u(rY`H1kS!>yVeRZlNrURI-ZH?yCU^QyGF62~1eCK-%QU z0W`myz&Slg%l`d|Y)udD#PY9iW-wVcE>7xWEhFczBz1k(6dmeb#nkk$wj4C?=3BF6 z-`v6g0j=#3;5XIsVdd%WIo04EndHCyl)3N>w>&I}VEe==ujqjrSOx3Ko%DQni6;DL zw8}Bp+RAdYmFHV~#L{KSOF)g|YXdl~a^TBa^q@zLs76=dwCFNG$EJ6GY&hmNr>D{R ztjTuOhYRie&ROtVKcUvVP`B3OtK%T;q4A`u^)eDC4sWv&H2M|dAm(@atZ)PP+rB`bm$~>f^Q$3r; zZx_VhUOV*X?o>HcO`Cxwq-^H=rwj>FPHq505LR9iz!=5?zy$vH>sf^?FomAlSvpFaC0bHr-yI%|sXn4^ceF>QZF>QRAAN<(->M%ClC zCne?1p97*}6bAPri|9;_M8Xl9c;Zg$Js`bDqRe}%h@(OP_Wj*7#6Ra6j&i_UVdJmm z6MF?lI-K}7kJes7TnGD0YXZqQWvDOwpL88C;x;yQ#>DBE@h9o}8So<@(@USv+giGP z)}|v+Rj=9>7Ri#6EhuBG>k%l$6ak=!N(I{u4z zEsNWXn4R$p8K6H&rcJ6b`+@o`Gcfr=(3=ovp)x834isSg< z_mzQ@y5NLC-qf!D)|RIY!f)brN$w=eGy@naaFXRtI!NFZx8=1@n%@4OZmx2u`Q3B_ z=+W9hF9lHsAK2!ABzh|vqcsWjpaLvk%%$316e!tc?ZLi9rj?~-mRk$Sk1RKU1@I8x z*HhwXaLU9@%51BcJkYQ};;o_VwbNiT%MwyXs41#QH+z?Y>F|dBmw6LgKyZf5tWN0| zW!A(bI4@(?;tK&tHTP7(0;=(TFXw0gmG&1i+R{?8zEj~{rFhEeDilb<%69iRglQh{ zrQ3_PVlpMNPIm`r5(*C1ZR2chA#ynrswwJ%`2ZMe49m2tq4WksgwD}GQ);@r3oz{n zciAhod*}5x4i2JP>?3DmHrtp7?b6985QBMez1LkN17HKB3kAD3jCH(s`E#udc-i`; z%4Ff5f4(;>OPbk9L}|IQ=b@`Q0L?v88}I6QB$i=V(E_Nh0Nb+63UIO;a+WlvmMXYlI}ZuL&SDMcTh zlD%0ia-FZ#)|T+u2`&~UZerJHU0+a6qdj-DcN)DL)-!Lb10c^S>NbT!VKO6t&9SQ7 z3WrR^8D&OB{r-*0S#ZJpsab)((P-PKn3yTZC!@?NtLvUT8tFlrqSlq8Ug*8&dFb(m zK*=*FR_+1n7rL z(%1F8{^e5-k64p%P14EkuX;i4oacHM&58z~@%+1Z9P+5Qq0g{IRa&Zp| z&IZ7*$mJnL1;@H2H>BJYvp)-r|Jmi02?xP^Xkl*zoCVhy5kx2;yc?%=&)+y5H+G0O zoCQ?uel$)kU5kLH+uxSOD|&V%IWNN;GR09-oeFQ^FZAEnARE^k0;BbcR75~VnU_wV z+!nc*(G3XpcNNUtaSLqO@tN(`1o}ybpRjhH`rHy7??L6NVri9vG|g<99PFL=%TKwf zHJS2t|Fi(LekjXS-fuI?%xwS~hMWcW+HnB3D-o;M#nxP4i)DXEpzqE9qJwmPZ&M$4 zCM27@OOh;@R|~A30GMD2Cv};3GqI0=f0#PsNU*iyss!PYNaR?|#Hr*81!B4={b20o zPp_Sbe(v%p^L+WX6Y%W%SBRwgzZwEXq|)b$fySJs6e%jQSQ@afqx9EZrRV^lW8%w% zy<0&Jgpk=6wbCtnz1G%9V!GyOzO-kEcwY0$WJuSnH}~hI9i)Wy$lyJhPPEkXOVW5n z0v%iAbhc$z9`-BG8)TkMH3!=}<$=D_?XS4vWjArgw{9nZZoLqm*TL}smsK-WF{DJ* zxU=Sm#nP0s03S8vL>=AwB=N*Aa4ZYVlT}Z-TPsgDeX=|C!>p1KjFG=y>jQ{o&T9bR zXvgbn6udjy{=%cQ4@~1z%;&?L73UIP>Fh%A5c{Z)9hXmU52nE?1TLn(^>||G*I`2- ze{|N5&6EO;kj^i6=)%3>ZEIr5z?Qj-jW}Gh!~voS^U;MA7e@0GkxSr;5m5Z(>u0_X zYtokU91^xUp0kcY^}A#PuMxFb%CDo(Y6qSJ8bAOBvBlsGc{jXGF8K-`1$RGMXBb$B zci#hN1K0)Sggl911*IlpG!7S$$QgsAF@fHc?RzbX_Y2Yo02^gf zZ~!X~6Fvjq}DN_9Dg*SO%Sef)3w+p2R^IBWH+ z{^jm{w3hl&i&lLDvUTA3B!qkxaeg3@&M#XJmoQP?UB1%eOi%6+E|j05^uetLGLdJw ziEorc{jXe{`KZ1gxWJlG4+iaG=IEqxB;~ZDy8yO6wIMKjRy^_>K&lJ;61=`2jc&Y5 zw_A2IKUDUAzWU}k9=_@78FefszNboJ|MUrin+)}VnW#;6(@wbu*clq*(`c%RyX+^e z7Xkc32D=L#E-H)!8y_6!$nJvVlw+MGcejBaZ5AVavpY4no#Orv DxsW1v literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_nft/00005.png b/tests/ragger/snapshots/stax/test_nft/00005.png new file mode 100644 index 0000000000000000000000000000000000000000..392165d4fc71395ae212fb0ca017ebdd204c126b GIT binary patch literal 6218 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 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_nft/00006.png b/tests/ragger/snapshots/stax/test_nft/00006.png new file mode 100644 index 0000000000000000000000000000000000000000..339db1b45cb46ceed041751e09147f0574884fa8 GIT binary patch literal 10991 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 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_poap/00000.png b/tests/ragger/snapshots/stax/test_poap/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..751240cb5c2ecb81f79422466d09dd0a187283a8 GIT binary patch literal 12769 zcmeHuXH*mKw=PvdDHcRJDn*b;?*tGrB1#bpY4!?8Oz4y~SYu$Cux%b0=K4fJwGw-}J@80{_`+0Vr-Lx>_ zJtA_1g@uLJ^xBnMEG(?4EG%rVxY&SSbS<09SXflvnO-rn3Cmj{kutDm4RLM}DN3xm z9J(Altj6uflsR~fj8l)D;ovs9oO(?9@yy}ia*<6z!*hkoD-2z`qO5ZQTq#fptBZYR zXW%kx`wu30nv>F-h9-~_Uce9=OtdYqYp$l1X(oxb;7^i z!~e2};@g?-_L;$1Y?4pgKW+5lrfJ*%zVkVu@EsP|=6-93+OK(Wfy~O~j_gPg=uMTB zi#!HAu}|dRdU@|vQX}xBK|7%+3AUr@@_jh$1>*xrYnBsFI2UF^qYI9=3m~ zJ*2t44vs;ZuKnf$fn^BvyEii(IJ|CVMuI``aj!K&wz8W$+( zo5mW}1q$70y{pc4RO&fm`mI+lWJoB!{gV)3`dMrZP3U?1rw&ZpYfmcyCxPe3fq{L~ z)KE#NZY~Z%3AM_qmS;1eNtaw_RgBR=?D&SCsO}=*obRjcWmxQ+7;GgK@Amnk>F;;@ z12C3%Lu8^NKPI&eMb4^I%~@bX6TltHc}>-S%s2$&yv@s+D$B5Ge{F7G2;WaDLq23i z#~2ix2c0(%GQZ3-yzG;8l)DeN%q?Z&qxWJalm~cg$M97x*bCuJSLpX=Q!O?;e8p2t zBFHKn=9j18-j|AtdJ!yJT8Hr3B*&>0%|4u0LLvdedo=VK-qK>9B(AEf*!Tw7Bfb}K zI`)a)u)2npk!Hz$zvLj9;N*JEbykt>XlR~A2?+tmT2vtA7^J`)q_&vni66j$?ashF z_nf3m?|~wCQ~#WHD+ytmmZT)P$HR8?(Vx~B-TXmHaBwQ+C*L1s9s-5FbRUg-^~aM# zaJ5=*5=AE9%p9JE#AL#GdBtx{rR|LW-rl|Fvv&HJ-3KY24t4VtiQn4E1WN75Q?(jo zLtKV*&t}B)!Y|D@GHsiofBNV5gDJ?E-hz()4y%h#ku?o!bo!)RS7yWXIt3ZpjL;Y3 zTg*^5CsL7C0vjLhasiXNVr4@F_EdX~oL?qafclmV%EwX;8>PDE=mS%0PM7X^ z%IfszkhG zNIDL&@;Q+c{B&{XYsHXYx5}K5GMfe{Y4(1d-n=3WMpI2@Ejn%|B%gd&t=CPvOXa&T zhhUdzVAEoN_E(|Lz>;;*QRLRR9Yx@fDTg5D1Z1c^?lp~ZA2xfit1&<9-lgJtfQAoW4?7h5q^HidXgPO_)$&}mvT$S8 zBjyp33MESG7NSi zyFP)jatUC9K7o#knHl(F`nLg_DSmacRR!(}v6^qbCGcd25eAOHXweQ0;LLt){c0r^ z$_=Y;f|a<~kWXC7@s7|=zM=?nwpumdoAgd~^M|7U z>=y{#ExREUXLQU4x)APcp9xDJMdni)^Bgx&hCChG$&O`rWj71u zj&xKb5$^WrLraK;bi;(g)H@tTUwzvR9C%9O`;KTPo~4*B(48LlSV-JugAFc%jA$QCZne-y80Mx4~S zk>`DPGOnug=WuImYkLz3!DN+;dwo*SI5|Wuc3Id$1ZCG5do{c4r!?%o~-2+ljc3CB4;?nvrg8kkXX?Am5(CtH>PcB-mZ2)mGz z(mxy2I9^6uz8nG@WrSY1e&qY^9lfQy1vP0A1@dtdl6mI3m5eZj9eZJF-&go` z+k}{YX7o*M)1mtP=ZbMX<`g8{&=Vm>|JzRM>#r_5*U{^;ato7Uts* zwm#t_G_h-;3L=ZdjtP^Mis{{pzb1|FTWv#~%2j%L1v#%Lj8@Xd<8M-bpe2r!W5_*I zD?su?Wj5jnC!WESV&GP6|U@+%7cB6Mn@9@@MGz#N4Vw zlH!VJzfSpV$t;!H$Mb0&Ob~u7Lp<9{hOy7|XU*n0^K^Xe@6C($-rHU+eRn?5ZObt7 zX?c#D07Q0ZnIHWuGD78QZG&05`Y8sXKIdd`@)<4%hLM@Bo}~IoJ(rvN(-IfmpwPuO z7}I6=aN=g~S34ri{N<$vCnF(e9w0x%S6x$>)? zx1(AkElv7`(mPNejYgA8SmsrQa<5cXQ4hOL9`!k-IK;Ey)xz0cBpO_2dYz$rLo8kf z6+7Vn#Ug&x8yRXaSe!Ixmwb4Kx2AhAn>$W`)^q2ImGq5%`ZPCa!tfrOT6G!rf?B*i z+YLp1HK`=oyxO}BQ$0^LY2=@pIs%W*q_Ud8%Jy$^uXxP_AMb3JiF1>)eqY%+Rk@x0 zrmjj2p1uZT0vBZm%dqHqYGB%@N}F6s8G3vj2;=gTbVr?fbMd;3Jf?KMkHZ7E_kK#@ z8?wcrVvLGJ7Y{5>DO}}uR?O;{{a)??g@$ANHFHP37X;>*GEtA-lJ1p~C9!TgjFH&@ zVDcY6q84d(QAAQ|>SGeO-j>f3fq7z#N z9{E)cE50+88;v$SHesJ{Oqv<(5^s^MHY@weF}AikdOq=N7VV2$CZ%!b$3XcTSsA~= zk)@4czH*7h!eazFUwnr&iO|7mU!9c@n%p1P>?K3Yt_A5`rCg1Xk)mMdi^Z8OG z-F>zZ8%ZDi-Z$YDeSV4@{u8B=r>f2VH#ZBV|5sx(rE{N2tAAV(T+KL^d-|^4@%^uP zj@`kNqJH0h(uZW~A|;CCg5nG&CGTsJNEN#G;=Pi)>dN8=n^LB-ng276P`BG0--I7; ztd){AJ$JjEx2>)>2M2!|KzQp0c+^f9JuKLJ5+<{JM{<9rP3=vH+o}Y2+kT6Lo@pAW z4+qi%($uE+V?Ul7bS;~oj+>ngNbQ`c(vuXv{fYd(iWZ=Kt)PYZqv#-z28vV!irr|- z9g+DJ_3IRX02i-jZSiIb*|m)&kN?$}_WQW$VzN4Y5(huJLB2Sjb7#E%(U_?6cx3+v z{i2DA5g?1M)Y-^m=Y9B!`tUV8xn_^=#bTgF<>SJADs^&^=~y2VzhrpvYt!f39Al&d z0M)XymCSSts=#7B3|~xJy+Tqf(&j_(n+46mht!`V9vfOGd7Nexoty@Wf;>^k<}`0j z#`IbP^8Qo0g8nJt_|fIcv~C%vs@s$b_;ooK7rI{B8*0KQ_Tha zP)Xf;AaC`c&5N2VeoBoVsZ6ZcvruUQ#~gB~40-utnr zFrl%ZasG>zB)b(9@FrYQZ|g!02@H526grWT^K6a*mI`*_^Uu%Kh-tq*OR2%iSKD(flLo z_#%uz|DlawqJdP9`{9{tQ6-4gwLTdh<8YX>ic(kLbP~JH+B!JP8FPlK|F{HXh-7`& z3w`}$551YZZ?N!fi%yqvPcHldz6jJxG@5^H`CiGj4EIKdig}uLGD9?uOeUv2!jlwD z(+UbC!&^3mZ3)0^5@lznvOA0?r3U86>L1bk-_t{L;oVJ`X|GI~=fz`EBRfxnFANMt zef1b=FPTS6NaB0ELNj@FxIMc=3HP>)gWca3Q?V3o9HE@cIYD+741h!JMQiA2L`x-g za`(O-O;>!lBWpCm8APBDc+h`NJAZ!pb(?vXG`ki2EOPK~&l!!#_p-d|o?daIL z#zMJX+;UK(b~i>nq#tjeIZ=Nxn1kPrngm0UHV5N8h;(bS$0EOtk2a z$t^1A$A?-bXlQi%(L(gnAEYN0iVY$jqa9L6gW}us+has?&ff6x7JCGSQl!-33p?H{ z!fpcpI?0J3zDCpd#1_VNrz(Z7Z#}=(ezq4^Cwg6V=GN6nG+UChd|<;_kBHJdUWx7+ z9qw#Oc+7OO8$?+wR^w4TAVU>V&;m8$UagRDEe75iF$RDo`&N!LTs#rj!h22JO}CAA*usdRjEv^@$6sqk3VGkwe}}zLT4ah$N)A57 z1p-|bfD}VNkZBAW=t7a(0?nf?=Z3640f_7R6Wg1cGBIK)-LawAkIg^tci+kmv4;_J z2a4hMGlXA$XGGU(zzdkmRi!yYe*O|kaYJ074`!kOixw);W12S3y-ueGgC$nRFZ2b~ zPGr}ui_G~y(J>6$4muY(e<9~BTP$Z5DZa(IDkeJGL8&YH2~fZcQ=4XJ+kj)ZxVWs- zfS}+5Bs&v2LBIphyC0D=Wq-Wxe|`qrG{f@3=HLon^4^BqNjzxa-ew&DdSqg;JT-JehU${Sy@F+~DGCp}BTL#hMRPVjVZ@a(FL~n^Dc<#jZ&K>5IDw%^@ne22wsPYw-V4nDR=||oXRp2Ks~bn-Tb_wbIEg4D`QSY9iK$1 z&edOJuR*nhb0r;~-vz1XtaW|v{W2f?JnS_JU7n@>d^xi7BZ6mNcMaZxkCOqg**-H+ z?6A>I0f>DjFwiz?f`}rMS3=zF1tGJHR*rO1`Yqz7cRdz+fs2j18;eaS{Ij#u=%hGM zS-2SigJuMu@oR9@sW}C)5{g5@PD5T$!tFB$!&M>!-GtDpRkjND(9p< zpqcIi#rO9GsVBMv4-j`ey~iOEb6emEsbxF{?f+dDp_yAKmnQui$O-Q{ft>?BetAiM*OkduzZ9_qg9E;S}Z`E zljc6ydqf7pI1v?YJr}g;{W!Dh?$}fOel@Z=uWJS9qf&t= z-_c#m8aQb;K>slq`*#ZEJI$+FJCfUptY1gZ@yxT-7TpviMtm|zzU$?`b-Fx!e!IW6 z9;O+PSYKIm(l{ujDkl{8V}L7Cy&Apn(sqV4E6?7>w;QOIbU(`$N{0|c0w<*i!313? zuk_RIY(-^icz9kcED6);ZXeREkAlu> zJb}OQy1?!%*FUr~&m_*-&E$uEoo=sU zCkWZyM#?(cM0@d;0|j(HtF1sMQ0eSxB=>(WFO1TW#vbqDOx<}I?6uasL8|yDHXq%9S9_tD3lM3#`Gs=7cAkEF zlxOkwd=J<$IA9~JPhwcfgD-kyHL>toQ-kbMdqme-6+6F*)N_4mnDC#8MOJr$pW-O@Nv;CJxC*ZlXmyMpKS?LGF>AL~k_AYoccEMX;p zQyx;CUH7!8$H7(oeJ^TN-=nIRoplqsPK;AOmxRT>KVSzX((G?NQ8AW-SP2X&%(O+$ z*+6Bs9aOd$Ez=g=-{P8%OiP!2&4Xhos{O;$@@^+iTn$>An!7Ahjjq}jYE9-B-a~#@ zJdZ8T-`TCvnAI+qO8~O`T(xxw z*Vy!aW(`?)dZ;Z|e*^idd!-=7K6S2QyCY&_4NpQW0)h3ZwS1}+8B@G3Yd&z+rewsl z3#yrty63~qI128mf6U%sciLVllA|>BMNS2sCcm|Y+!YtJYvYd=_uSm$2;V=^=1}G2 zDAKrCNxdk{s$5y*aEQUvW-i*8q(fQ?;Wo{3vndzcj9OmA=vAb}UW( z2Fu^`IqeW7dJkqj$}w%hLmYyy#+@!2#p@vd{Ng-r_rUh3slsne|G8?G48aWBAG|pb zSJuBE<%-W`YKi|Qy35s$|H=sSk^y!%?0p?tL}EETRT;d_abd87-;`TUkFs)uEewFzYcKd*`h6_f2<6?LeY=fG*OZ&m3`21U856 ze3|1!k+ld<4q>iH3OXk`{${$T6H8rA*+HQ_{JvCt$ijQdi@s3FYf>$Z>;;b2P^guF z#}EO07K*WmYDiENeeI*l(A-19w##WBPSu@Ok5bqypEP#tx5s1rMxnm`?T6}0#9B_v ztFHWx0ZX``G6Nz`&Wym8L-ZnR|F-%|Tt3^6>h*8PWzu~Rq?PG%(#{9%dtfx5 zD{luLRuao{{zMr7TFPQb!0KVL_n&-HJh{9@r>3Qq30ZOY82^4a6&iUz(cOM5Zw4$m zkEoF(J($}n1ofp!3x`?n%4Bb?E>)0+9@DjoTWE~WC_4soFARKeZyO|7V!#-c{|pcj zVZ`+abnyWrC`#TQQGY&Xhq>*P=}teuE-*ulr=zJdvG5DM8Pqqnn)VN@=p1?z_U{9s)#{gj|d*dZ%g>Bgw-l~cS$k=n*nrhRChE`fGMU7t` zTl+8t@fKgU^;0mtMA&q+r~C%psG_1%qK4IE8L}ws<8J@H>-VtP%v@#t4;?q&8+`2K z?UhRzEgz$u6LlPxOJ0x1r!tF(QRRs_!+Yj?%xwW-M*KIkiN7{N@^%6ccA~{14g3)j zG!gA!Molm6ugaNFg}J8->nequs)uvN`@fw%xVfm+GPzsp`-he zpW!9$%2ed;_00bXbqIEz3T{HXw?@u~*00C;zKty#vrbZ!O3=TE||%eX*P3zt$)bgFx(E!b@Dk_dBQTJxQ823yxfuYJ9e0KhAF##Qh)ZG?}8x= z{!~_4tqmX3cdw!h6whSn@vS0&No`3Soqk~V@>#q_9s#a%!%m>{cbHR!El^)En9-AO z-+29{(`y|n-ncZ%9jU6Y486>ran~d;N)&Vi@|vBL9Bcmrm)yFX`KSXFc{ED7Mtf4czgv-Bj3bmk47q53b$r>v`u%v z&*OdS9x>W(rBj14D5xTq*?h=Lt{9rPVJ_WIyA(xO^$E2i<(DJP7hEG9b$Q6w*>A7t zXV$XTm;osv6zYZ6HB3>h>&~r|Rib-^1UGM_rYmB`AikQS!Nw0UJ<>)qV^F|9d7bLF z?FJ7yiHvUcavwExcYW&xh*<8AF=vYow=6}Ax*OmaA`mO>s%aU~K-U%}o(^T60Z-$* zZDdNgGqL{V1)dJM1a$gBcr;i-pCC#^px(W^^T$@?gZEs*?{}yD+HI#+wy0E}v(Otn z8Y?#iCQ960HxLbKu;wjyG1Z5~I%8J{9krBRr|M+yqtC{;2~FPc;?rJsNIiS%m<}^0 zRBsxgrE?$90T$4I6ZNM{b#f-Sw$Alu$iUtPb;0We|2NB}O2&_Fwt(wcO=uA2quW`g zH0^S3ie{iI386&++Ysv?7lqaXwesTkS0hW(9k!B3K=`^rm1zy;Jn@8P*06|O1!TqL zW31!S5LC3avy0Q#$E`{8yISN6UrNX9*EYev--R_rO1$MgeY>L(s4<|LP7@7nsm!vu zdTNs=CCby5Kf@Xg35|c#do}ysJXW6&pYZpmtw^i4|IhvC&O>$Pk^YUhBZua=R&v#M zEl`GSOa7Pl3xQbQcw2h8jNNCFEtuW#(n-jiN4VnI2Zqnv*}lE%20mZryf1Yxc$6FP zUg@$cD_RbFuch!EfH;It_!*bmV78+-uX-yc4wRqXbOxmOgUaX?;f{j6QiXFk@bw#; z8g-yl#%{I6K*5MXp=*mKfKrvBZCS5uM$0gh<(Ch`U3=hNjH5S2zip~C z@AQsyuHF7t9lftF__39{q1?}s6B{Ffzav9kk9zTd5Ks#W$E2YB?2LhlxBO}&ez@PLH)IfyU{(u-x%GT^s_ z|0!Jvn}M0ydx;}T@W0_j*Ui9uW$|yG=}76U^~~X7(Xlf8aEYZJO?I>sQaO`*UAz(Kn3FZ z`Z@LQ9tSl`S33}>5UXzyn$<2;55EdwKxP6cHLyQ#Id>vQ)B~B4TPax_fLI(tI=|bU zbM==tv)0>B=Q=t4bGjPJKC^=GN9>j~%hBz$b|7Yj)?SiLt$Zh;8jFIx=9b52zyaPm zKs!n9#Dgcfes+(*V1OANOH7AAp(iXusFu!N;+W`0r{-?LDYmb72B-S2>3iZyA)^_Z zxz8y;A~y(i-6q&hv%MU^p-K6y$|X{us61ns6cEPQ61<=_Nc0uGqbVJ}-4G zmS2<3yF2fE90~Jpm;xk6fa`XsXl-i))Ur1j!3s}2jt&iJs7kaS&fZThvd_#Xz?TSV zthTCu==XJDil-U#K2Ydz*fKY0NrbMuI(7I`{WDtprgU&i^03i47Uy56tVN!WU)Wjn z7yVDLwO{%VNr3;F6Xs$tl!b7EP!*Q%t^47a)7`(PJKM|V!w0t$Q;hzb=#BrwlYMVJ z=uJc81vmtq0RpB_Zc&l5tBe`}EQM{^gc_C34)nX>KorGJat5W55{`uJm--uxHB?d? zkE%W&@UVtvd>(7g*=!Te^81Foh_CP1W-9YMIwPb{`KJwq5*{c9x7TcaD%2TOu?`({ z6mqoBJn_mj%}kHdRJqM)d4?TXnrG}82VWccMsHT;$*S$dfzscl^*1>w62vkHc2``X zP??%x0DnMzU4CaqPK0j~@Lscrjn;nh{ViRtODOc5`81x~T{Iyn%MUVDAaA>;pzgz! z-GQgjiCtjqEd<$L_K^2Hk?Foer>iLj0?MrK7yuQ>#=W^P@O7#EFmUUABL|RL1!M*ss1t7jCXEAzPgW!G%_<(8o36)0Q#xZmMQ`d5#b2{)muRvUEsT)6WAAydD4 z@Mw1OYW(zL% z;e_wu^pbrFquiO=1}bJ}RN-!A zzr%f3Kzaf&%YTP6y=_E2Lm+(!x^A5*-Lnw%f;-}ZeTQLgaj9t;byK9fX8ENoY5ulV zvgOHQqhnXBHrz~>VUl^a?)KM7y}P#D6Ozp__>XG$_^J$E=r`J{`q<~M0s4p+W@&o0kiy{^sa+T3Vohw%N7?6EGGSoaAyp`V;5oaZCA zsFr8&XYRLW^q|W<``A%{=dxx$Eqfg;ZYqmtvXJN7FP}J`i;_&hu7;)6{c+fqv2GO7 z58jF0K)Jv+fkfdbD4i3K=I(85aDs!kwpZciu3o!Kk>`U&oGp+jXPG|g%;6|<))3De zc4}DY8-oFF;2@vG_ZdYUD zkMosX+=5+ayz75SCTQ%|7K{HflZUpggiPsv-B40BL5WulTTra;Y3etRx#Qt{MR5zpf{Vc#8zgPtd&^+One>+_mnUr$Ja7@FR?CWjv zRQAMnf7+C?IzeJJ1mCwX+{Vwl?<*9yp6g{P3qO=M#oC(%4@C|{t4#=_?51vBeSXO3 z3=#HaTtRryAOOUGXX^tR93$;7B{xg=qHvuDBF6(C**%0I=tiuLxvCWf39fhKQg)ok zG!wp-P#46z(Bxbl|>(F6L@~#gfFoobF8WDrMIdgfq*D*SMO*nU; zYxhe`*HB?HNz-Xr9}|$(c(lFQYXKGFI?=Fax9FPLc1hL8i~n`I@%-x}wLnl1Hvgo! zh**6y+FTc!cDeAnE^YB*&-ue+2V6T>d;A(IhMQSiFfGlo=Jx+uW!dL{C=9C#fV`0h R{<^_p3bD9SdD->h{{U-|L`whw literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_poap/00001.png b/tests/ragger/snapshots/stax/test_poap/00001.png new file mode 100644 index 0000000000000000000000000000000000000000..f73962481c41c7f7d3835d34598aa16072c22cd6 GIT binary patch literal 19955 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 Nc)I$ztaD0e0szubWq<$x literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_poap/00002.png b/tests/ragger/snapshots/stax/test_poap/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..829c038b454e177dc5abd922cb2b86d28459fbdd GIT binary patch literal 25661 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

Nf@L1pI{plMVwT5-5UXeY zB7nZX9g0u8Z&Hby$JFBkwZ&Yk-yuEM@4l=LgxWBsZxi=QU4>=1;U*+aItUb z43BF#SUUl()n0Im0|2fnY+q0ht-%i>+V^*N4J_3>j7B`RJ?6DichPP2>ng4Cl;f_# zK8ztdYp1KJl)ZJhUUCr#_k9frs`zoq&O4M=jigS%`$yYMh^e9W^njvP%IK0AY9x2G zZu$#(|4*H{ZA|-ATW&*h7xLB5^iHqM??`0hG>u3k0+Jf=O;Fa<&DKWF49V@C-+xH( zr^L1C z!e(e8Nj>jp6$pzPzD*e<{RkL9O@BJZxeYSRRbdmOhjGhGH6UDIgWb4>wIx4BbyLZI zD>9|X{YdW0pD{lr;d*C1=64bBx|1!jlBhw<>kOh5w;XOgOgm4GyHVo+tHh$$S zY@qJ7&n-Hk%J6xQ3Q$At?0CWlXDv;o-Ze7s9CwK5!|J+hMdHuholY7$`PJnlPBR0U zRJT>H;zqvp%MIr{w$rQ3D>b|zH{G_f262USwC>`LRRQZex3x*xa&+a6O2AkHEwPih z5{Ks-vXi4Je=qM3CIx^6Gy3u6-`~3p*J?u=^;`pDT`?C4LC>Be9G|%L5=roCZcwmZ zgkK|>UdnfSDiK%p$ml6`;6WZ*=Y25r{Cj2Jk20619ZWX|!*Q=LRbA;~X4}&aN1CT; z-OApiv!iOXDZ^D?ES9dGS%m>*UrqKCFOhOr40LpY4SWT>DK8RdnYgO5N-Ki{GQ3ou zSK?h)GxkI092F#A<88?=ns)oy^PV}UxS;@%W$(cKX+FJAKbd)5a>+#uaR}$#L|xO)+*{!r%*b{}{M=&rEG;B*v0P8iY)qM#q9wNOvTQ=4 zGx1XK&hFLA^MM{6>!q%dbs*g61Hb(xLt>3cA0S--&vRQR!^OogdPvlZt$dEvA$q~A zhYa-eJQwvD)|0_j>^)G`PkmFg`XiL(ZGv{w0U{9Y4wK~THq2}MmTCbf){Ub?`XF0f zmW!QyIg>%K0X1Gct$|FZi;4G;sjQA&yD6m%)x!-7CDhhz+(&e~-c{w>N|9<=I%W5- zpI848j!>H@Ns#TCbbIq@6~ABx#Nm#Th3);5h?g^zF4HU+_BzF#5b0%V1CJ5Ps}2&^ zYYDYZ4Ff`M7V>U$Z&wd*J~BHnOxUBN&VONUmovVKU6|2$y#M`En^Z|= z-LCYUD!}@di+1jvV=5S+eEN3r4LW>R!g literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_poap/00004.png b/tests/ragger/snapshots/stax/test_poap/00004.png new file mode 100644 index 0000000000000000000000000000000000000000..392165d4fc71395ae212fb0ca017ebdd204c126b GIT binary patch literal 6218 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 literal 0 HcmV?d00001 diff --git a/tests/ragger/snapshots/stax/test_poap/00005.png b/tests/ragger/snapshots/stax/test_poap/00005.png new file mode 100644 index 0000000000000000000000000000000000000000..339db1b45cb46ceed041751e09147f0574884fa8 GIT binary patch literal 10991 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 literal 0 HcmV?d00001 diff --git a/tests/ragger/test_gcs.py b/tests/ragger/test_gcs.py new file mode 100644 index 000000000..a05c0c066 --- /dev/null +++ b/tests/ragger/test_gcs.py @@ -0,0 +1,544 @@ +import struct +import json +import hashlib + +from ragger.backend import BackendInterface +from ragger.firmware import Firmware +from ragger.navigator import Navigator +from ragger.navigator.navigation_scenario import NavigateWithScenario + +import pytest +from web3 import Web3 + +import client.response_parser as ResponseParser +from client.client import EthAppClient, SignMode, TrustedNameType, TrustedNameSource +from client.utils import get_selector_from_data +from client.gcs import * + +from constants import ABIS_FOLDER + + +def test_nft(firmware: Firmware, + backend: BackendInterface, + scenario_navigator: NavigateWithScenario, + test_name: str): + app_client = EthAppClient(backend) + + if firmware == Firmware.NANOS: + pytest.skip("Not supported on LNS") + + with open(f"{ABIS_FOLDER}/erc1155.json", encoding="utf-8") as file: + contract = Web3().eth.contract( + abi=json.load(file), + address=None + ) + recipient_addr = bytes.fromhex("1111111111111111111111111111111111111111") + data = contract.encode_abi("safeBatchTransferFrom", [ + bytes.fromhex("Dad77910DbDFdE764fC21FCD4E74D71bBACA6D8D"), + recipient_addr, + [ + 0xff, + 0xffff, + 0xffffff, + 0xffffffff, + ], + [ + 1, + 2, + 3, + 4, + ], + bytes.fromhex("deadbeef1337cafe"), + ]) + tx_params = { + "nonce": 235, + "maxFeePerGas": Web3.to_wei(100, "gwei"), + "maxPriorityFeePerGas": Web3.to_wei(10, "gwei"), + "gas": 44001, + # OpenSea Shared Storefront + "to": bytes.fromhex("495f947276749ce646f68ac8c248420045cb7b5e"), + "data": data, + "chainId": 1 + } + with app_client.sign("m/44'/60'/0'/0/0", tx_params, SignMode.STORE): + pass + + fields = [ + Field( + 1, + "From", + ParamType.RAW, + ParamRaw( + 1, + Value( + 1, + TypeFamily.ADDRESS, + data_path=DataPath( + 1, + [ + PathTuple(0), + PathLeaf(PathLeafType.STATIC), + ] + ), + ) + ) + ), + Field( + 1, + "To", + ParamType.TRUSTED_NAME, + ParamTrustedName( + 1, + Value( + 1, + TypeFamily.ADDRESS, + data_path=DataPath( + 1, + [ + PathTuple(1), + PathLeaf(PathLeafType.STATIC), + ] + ), + ), + [ + TrustedNameType.ACCOUNT, + ], + [ + TrustedNameSource.UD, + TrustedNameSource.ENS, + TrustedNameSource.FN, + ], + ) + ), + Field( + 1, + "NFTs", + ParamType.NFT, + ParamNFT( + 1, + Value( + 1, + TypeFamily.UINT, + type_size=32, + data_path=DataPath( + 1, + [ + PathTuple(2), + PathRef(), + PathArray(1), + PathLeaf(PathLeafType.STATIC), + ] + ), + ), + Value( + 1, + TypeFamily.ADDRESS, + container_path=ContainerPath.TO + ) + ) + ), + Field( + 1, + "Values", + ParamType.RAW, + ParamRaw( + 1, + Value( + 1, + TypeFamily.UINT, + type_size=32, + data_path=DataPath( + 1, + [ + PathTuple(3), + PathRef(), + PathArray(1), + PathLeaf(PathLeafType.STATIC), + ] + ), + ) + ) + ), + Field( + 1, + "Data", + ParamType.RAW, + ParamRaw( + 1, + Value( + 1, + TypeFamily.BYTES, + data_path=DataPath( + 1, + [ + PathTuple(4), + PathRef(), + PathLeaf(PathLeafType.DYNAMIC), + ] + ), + ) + ) + ), + ] + + # compute instructions hash + inst_hash = hashlib.sha3_256() + for field in fields: + inst_hash.update(field.serialize()) + + tx_info = TxInfo( + 1, + tx_params["chainId"], + tx_params["to"], + get_selector_from_data(tx_params["data"]), + inst_hash.digest(), + "batch transfer NFTs", + ) + + app_client.provide_transaction_info(tx_info.serialize()) + challenge = ResponseParser.challenge(app_client.get_challenge().data) + app_client.provide_trusted_name_v2(recipient_addr, + "gerard.eth", + TrustedNameType.ACCOUNT, + TrustedNameSource.ENS, + tx_params["chainId"], + challenge=challenge) + app_client.provide_nft_metadata("OpenSea Shared Storefront", tx_params["to"], tx_params["chainId"]) + + for field in fields: + payload = field.serialize() + app_client.send_raw(0xe0, 0x28, 0x01, 0x00, struct.pack(">H", len(payload)) + payload) + + with app_client.send_raw_async(0xe0, 0x04, 0x00, 0x02, bytes()): + scenario_navigator.review_approve(test_name=test_name, custom_screen_text="Sign transaction") + + +def test_poap(firmware: Firmware, + backend: BackendInterface, + scenario_navigator: NavigateWithScenario, + test_name: str): + app_client = EthAppClient(backend) + + if firmware == Firmware.NANOS: + pytest.skip("Not supported on LNS") + + with open(f"{ABIS_FOLDER}/poap.abi.json", encoding="utf-8") as file: + contract = Web3().eth.contract( + abi=json.load(file), + address=None + ) + data = contract.encode_abi("mintToken", [ + 175676, + 7163978, + bytes.fromhex("Dad77910DbDFdE764fC21FCD4E74D71bBACA6D8D"), + 1730621615, + bytes.fromhex("8991da687cff5300959810a08c4ec183bb2a56dc82f5aac2b24f1106c2d983ac6f7a6b28700a236724d814000d0fd8c395fcf9f87c4424432ebf30c9479201d71c") + ]) + tx_params = { + "nonce": 235, + "maxFeePerGas": Web3.to_wei(100, "gwei"), + "maxPriorityFeePerGas": Web3.to_wei(10, "gwei"), + "gas": 44001, + # PoapBridge + "to": bytes.fromhex("0bb4D3e88243F4A057Db77341e6916B0e449b158"), + "data": data, + "chainId": 1 + } + with app_client.sign("m/44'/60'/0'/0/0", tx_params, SignMode.STORE): + pass + + fields = [ + Field( + 1, + "Event ID", + ParamType.RAW, + ParamRaw( + 1, + Value( + 1, + TypeFamily.UINT, + type_size=32, + data_path=DataPath( + 1, + [ + PathTuple(0), + PathLeaf(PathLeafType.STATIC), + ] + ), + ) + ) + ), + Field( + 1, + "Token ID", + ParamType.RAW, + ParamRaw( + 1, + Value( + 1, + TypeFamily.UINT, + type_size=32, + data_path=DataPath( + 1, + [ + PathTuple(1), + PathLeaf(PathLeafType.STATIC), + ] + ), + ) + ) + ), + Field( + 1, + "Receiver", + ParamType.RAW, + ParamRaw( + 1, + Value( + 1, + TypeFamily.ADDRESS, + data_path=DataPath( + 1, + [ + PathTuple(2), + PathLeaf(PathLeafType.STATIC), + ] + ), + ) + ) + ), + Field( + 1, + "Expiration time", + ParamType.DATETIME, + ParamDatetime( + 1, + Value( + 1, + TypeFamily.UINT, + type_size=32, + data_path=DataPath( + 1, + [ + PathTuple(3), + PathLeaf(PathLeafType.STATIC), + ] + ), + ), + DatetimeType.DT_UNIX + ) + ), + Field( + 1, + "Signature", + ParamType.RAW, + ParamRaw( + 1, + Value( + 1, + TypeFamily.BYTES, + data_path=DataPath( + 1, + [ + PathTuple(4), + PathRef(), + PathLeaf(PathLeafType.DYNAMIC), + ] + ), + ) + ) + ), + ] + + # compute instructions hash + inst_hash = hashlib.sha3_256() + for field in fields: + inst_hash.update(field.serialize()) + + tx_info = TxInfo( + 1, + tx_params["chainId"], + tx_params["to"], + get_selector_from_data(tx_params["data"]), + inst_hash.digest(), + "mint POAP", + creator_name="POAP", + creator_legal_name="Proof of Attendance Protocol", + creator_url="poap.xyz", + contract_name="PoapBridge", + deploy_date=1646305200 + ) + + app_client.provide_transaction_info(tx_info.serialize()) + + for field in fields: + payload = field.serialize() + app_client.send_raw(0xe0, 0x28, 0x01, 0x00, struct.pack(">H", len(payload)) + payload) + + with app_client.send_raw_async(0xe0, 0x04, 0x00, 0x02, bytes()): + scenario_navigator.review_approve(test_name=test_name, custom_screen_text="Sign transaction") + + +def test_1inch(firmware: Firmware, + backend: BackendInterface, + scenario_navigator: NavigateWithScenario, + test_name: str): + app_client = EthAppClient(backend) + + if firmware == Firmware.NANOS: + pytest.skip("Not supported on LNS") + + with open(f"{ABIS_FOLDER}/1inch.abi.json", encoding="utf-8") as file: + contract = Web3().eth.contract( + abi=json.load(file), + address=None + ) + data = contract.encode_abi("swap", [ + bytes.fromhex("F313B370D28760b98A2E935E56Be92Feb2c4EC04"), + [ + bytes.fromhex("EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"), + bytes.fromhex("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"), + bytes.fromhex("F313B370D28760b98A2E935E56Be92Feb2c4EC04"), + bytes.fromhex("Dad77910DbDFdE764fC21FCD4E74D71bBACA6D8D"), + Web3.to_wei(0.22, "ether"), + 682119805, + 0, + ], + bytes(), + ]) + tx_params = { + "nonce": 235, + "maxFeePerGas": Web3.to_wei(100, "gwei"), + "maxPriorityFeePerGas": Web3.to_wei(10, "gwei"), + "gas": 44001, + # Aggregation Router V6 + "to": bytes.fromhex("111111125421cA6dc452d289314280a0f8842A65"), + "data": data, + "chainId": 1 + } + with app_client.sign("m/44'/60'/0'/0/0", tx_params, SignMode.STORE): + pass + + fields = [ + Field( + 1, + "Executor", + ParamType.RAW, + ParamRaw( + 1, + Value( + 1, + TypeFamily.ADDRESS, + data_path=DataPath( + 1, + [ + PathTuple(0), + PathLeaf(PathLeafType.STATIC), + ] + ), + ) + ) + ), + Field( + 1, + "Send", + ParamType.TOKEN_AMOUNT, + ParamTokenAmount( + 1, + Value( + 1, + TypeFamily.UINT, + type_size=32, + data_path=DataPath( + 1, + [ + PathTuple(1), + PathTuple(4), + PathLeaf(PathLeafType.STATIC), + ] + ), + ), + token=Value( + 1, + TypeFamily.ADDRESS, + data_path=DataPath( + 1, + [ + PathTuple(1), + PathTuple(0), + PathLeaf(PathLeafType.STATIC), + ] + ), + ), + native_currency=[ + bytes.fromhex("EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"), + ], + ) + ), + Field( + 1, + "Receive", + ParamType.TOKEN_AMOUNT, + ParamTokenAmount( + 1, + Value( + 1, + TypeFamily.UINT, + type_size=32, + data_path=DataPath( + 1, + [ + PathTuple(1), + PathTuple(5), + PathLeaf(PathLeafType.STATIC), + ] + ), + ), + token=Value( + 1, + TypeFamily.ADDRESS, + data_path=DataPath( + 1, + [ + PathTuple(1), + PathTuple(1), + PathLeaf(PathLeafType.STATIC), + ] + ), + ), + native_currency=[ + bytes.fromhex("EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"), + ], + ) + ), + ] + + # compute instructions hash + inst_hash = hashlib.sha3_256() + for field in fields: + inst_hash.update(field.serialize()) + + tx_info = TxInfo( + 1, + tx_params["chainId"], + tx_params["to"], + get_selector_from_data(tx_params["data"]), + inst_hash.digest(), + "swap", + creator_name="1inch", + creator_legal_name="1inch Network", + creator_url="1inch.io", + contract_name="Aggregation Router V6", + deploy_date=1707724800 + ) + + app_client.provide_transaction_info(tx_info.serialize()) + + app_client.provide_token_metadata("USDC", bytes.fromhex("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"), 6, 1) + + for field in fields: + payload = field.serialize() + app_client.send_raw(0xe0, 0x28, 0x01, 0x00, struct.pack(">H", len(payload)) + payload) + + with app_client.send_raw_async(0xe0, 0x04, 0x00, 0x02, bytes()): + scenario_navigator.review_approve(test_name=test_name, custom_screen_text="Sign transaction") From 6743966a0300b2a94655ffc53d2e9845c8518c27 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 16 Dec 2024 11:13:47 +0100 Subject: [PATCH 45/45] Added new APDUs to doc --- doc/ethapp.adoc | 123 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 2 deletions(-) diff --git a/doc/ethapp.adoc b/doc/ethapp.adoc index a8358a42e..058b89cf3 100644 --- a/doc/ethapp.adoc +++ b/doc/ethapp.adoc @@ -113,15 +113,21 @@ The input data is the RLP encoded transaction (as per https://github.com/ethereu [width="80%"] |============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* | E0 | 04 | 00 : first transaction data block 80 : subsequent transaction data block - | 00 | variable | variable + | 00 : process & start flow + + 01 : store only + + 02 : start flow | variable | variable |============================================================================================================================== 'Input data (first transaction data block)' +If P2 == 0x02, then no data is provided. + [width="80%"] |============================================================================================================================== | *Description* | *Length* @@ -143,6 +149,8 @@ The input data is the RLP encoded transaction (as per https://github.com/ethereu 'Output data' +If P2 == 0x01, then no data is returned. + [width="80%"] |============================================================================================================================== | *Description* | *Length* @@ -1070,6 +1078,117 @@ _Output data_ None +### PROVIDE ENUM VALUE + +_Command_ + +[width="80%"] +|============================================================== +| *CLA* | *INS* | *P1* | *P2* | *LC* +| E0 | 24 | 01 : first chunk + + 00 : following chunk + | 00 | 00 +|============================================================== + +_Input data_ + +##### If P1 == first chunk + +[width="80%"] +|========================================== +| *Description* | *Length (byte)* +| Payload length | 2 +| TLV payload | variable +|========================================== + +##### If P1 == following chunk + +[width="80%"] +|========================================== +| *Description* | *Length (byte)* +| TLV payload | variable +|========================================== + +_Output data_ + +None + + +### TRANSACTION INFO + +_Command_ + +[width="80%"] +|============================================================== +| *CLA* | *INS* | *P1* | *P2* | *LC* +| E0 | 26 | 01 : first chunk + + 00 : following chunk + | 00 | 00 +|============================================================== + +_Input data_ + +##### If P1 == first chunk + +[width="80%"] +|========================================== +| *Description* | *Length (byte)* +| Payload length | 2 +| TLV payload | variable +|========================================== + +##### If P1 == following chunk + +[width="80%"] +|========================================== +| *Description* | *Length (byte)* +| TLV payload | variable +|========================================== + +_Output data_ + +None + + +### TRANSACTION FIELD DESCRIPTION + +_Command_ + +[width="80%"] +|============================================================== +| *CLA* | *INS* | *P1* | *P2* | *LC* +| E0 | 28 | 01 : first chunk + + 00 : following chunk + | 00 | 00 +|============================================================== + +_Input data_ + +##### If P1 == first chunk + +[width="80%"] +|========================================== +| *Description* | *Length (byte)* +| Payload length | 2 +| TLV payload | variable +|========================================== + +##### If P1 == following chunk + +[width="80%"] +|========================================== +| *Description* | *Length (byte)* +| TLV payload | variable +|========================================== + +_Output data_ + +None + + ### PROVIDE NETWORK CONFIGURATION #### Description