Repository: LedgerHQ/app-ethereum (language: python)

Issue:
Fix recover_transaction function in Ragger client
## Description

Wasn't working for non-legacy transactions.

## Changes include

- [x] Bugfix (non-breaking change that solves an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (change that is not backwards-compatible and/or changes current functionality)
- [ ] Tests
- [ ] Documentation
- [ ] Other (for changes that might not fit in any category)


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0bfabaaa0..9b3afd163 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](http://keepachangelog.com/)
 and this project adheres to [Semantic Versioning](http://semver.org/).
 
-## [1.10.4](https://github.com/ledgerhq/app-ethereum/compare/1.10.3...1.10.4) - 2023-02-16
+## [1.10.4](https://github.com/ledgerhq/app-ethereum/compare/1.10.3...1.10.4) - 2023-03-08
 
 ### Added
 
diff --git a/src_features/signTx/logic_signTx.c b/src_features/signTx/logic_signTx.c
index b1ca0654a..bdacd7684 100644
--- a/src_features/signTx/logic_signTx.c
+++ b/src_features/signTx/logic_signTx.c
@@ -316,15 +316,13 @@ static int strcasecmp_workaround(const char *str1, const char *str2) {
     return 0;
 }
 
-__attribute__((noinline)) static void finalize_parsing_helper(bool direct, bool *use_standard_UI) {
+__attribute__((noinline)) static bool finalize_parsing_helper(bool direct, bool *use_standard_UI) {
     char displayBuffer[50];
     uint8_t decimals = WEI_TO_ETHER;
     uint64_t chain_id = get_tx_chain_id();
     const char *ticker = get_displayable_ticker(&chain_id, chainConfig);
     ethPluginFinalize_t pluginFinalize;
 
-    *use_standard_UI = true;
-
     // Verify the chain
     if (chainConfig->chainId != ETHEREUM_MAINNET_CHAINID) {
         uint64_t id = get_tx_chain_id();
@@ -334,7 +332,7 @@ __attribute__((noinline)) static void finalize_parsing_helper(bool direct, bool
             reset_app_context();
             reportFinalizeError(direct);
             if (!direct) {
-                return;
+                return false;
             }
         }
     }
@@ -358,7 +356,7 @@ __attribute__((noinline)) static void finalize_parsing_helper(bool direct, bool
             PRINTF("Plugin finalize call failed\n");
             reportFinalizeError(direct);
             if (!direct) {
-                return;
+                return false;
             }
         }
         // Lookup tokens if requested
@@ -384,7 +382,7 @@ __attribute__((noinline)) static void finalize_parsing_helper(bool direct, bool
                 PRINTF("Plugin provide token call failed\n");
                 reportFinalizeError(direct);
                 if (!direct) {
-                    return;
+                    return false;
                 }
             }
             pluginFinalize.result = pluginProvideInfo.result;
@@ -409,7 +407,7 @@ __attribute__((noinline)) static void finalize_parsing_helper(bool direct, bool
                         PRINTF("Incorrect amount/address set by plugin\n");
                         reportFinalizeError(direct);
                         if (!direct) {
-                            return;
+                            return false;
                         }
                     }
                     memmove(tmpContent.txContent.value.value, pluginFinalize.amount, 32);
@@ -425,7 +423,7 @@ __attribute__((noinline)) static void finalize_parsing_helper(bool direct, bool
                     PRINTF("ui type %d not supported\n", pluginFinalize.uiType);
                     reportFinalizeError(direct);
                     if (!direct) {
-                        return;
+                        return false;
                     }
             }
         }
@@ -450,7 +448,7 @@ __attribute__((noinline)) static void finalize_parsing_helper(bool direct, bool
         reportFinalizeError(direct);
         ui_warning_contract_data();
         if (!direct) {
-            return;
+            return false;
         }
     }
 
@@ -530,12 +528,16 @@ __attribute__((noinline)) static void finalize_parsing_helper(bool direct, bool
     // Prepare network field
     get_network_as_string(strings.common.network_name, sizeof(strings.common.network_name));
     PRINTF("Network: %s\n", strings.common.network_name);
+    return true;
 }
 
 void finalizeParsing(bool direct) {
-    bool use_standard_UI;
+    bool use_standard_UI = true;
     bool no_consent_check;
-    finalize_parsing_helper(direct, &use_standard_UI);
+
+    if (!finalize_parsing_helper(direct, &use_standard_UI)) {
+        return;
+    }
     // If called from swap, the user has already validated a standard transaction
     // And we have already checked the fields of this transaction above
     no_consent_check = G_called_from_swap && use_standard_UI;


Test patch:
diff --git a/tests/ragger/abis/erc1155.json b/tests/ragger/abis/erc1155.json
index 3c53ad8c2..6f8e6ab25 100644
--- a/tests/ragger/abis/erc1155.json
+++ b/tests/ragger/abis/erc1155.json
@@ -1,276 +1,276 @@
 [
-   {
-      "anonymous" : false,
-      "inputs" : [
-         {
-            "indexed" : true,
-            "internalType" : "address",
-            "name" : "_owner",
-            "type" : "address"
-         },
-         {
-            "indexed" : true,
-            "internalType" : "address",
-            "name" : "_operator",
-            "type" : "address"
-         },
-         {
-            "indexed" : false,
-            "internalType" : "bool",
-            "name" : "_approved",
-            "type" : "bool"
-         }
-      ],
-      "name" : "ApprovalForAll",
-      "type" : "event"
-   },
-   {
-      "anonymous" : false,
-      "inputs" : [
-         {
-            "indexed" : true,
-            "internalType" : "address",
-            "name" : "_operator",
-            "type" : "address"
-         },
-         {
-            "indexed" : true,
-            "internalType" : "address",
-            "name" : "_from",
-            "type" : "address"
-         },
-         {
-            "indexed" : true,
-            "internalType" : "address",
-            "name" : "_to",
-            "type" : "address"
-         },
-         {
-            "indexed" : false,
-            "internalType" : "uint256[]",
-            "name" : "_ids",
-            "type" : "uint256[]"
-         },
-         {
-            "indexed" : false,
-            "internalType" : "uint256[]",
-            "name" : "_values",
-            "type" : "uint256[]"
-         }
-      ],
-      "name" : "TransferBatch",
-      "type" : "event"
-   },
-   {
-      "anonymous" : false,
-      "inputs" : [
-         {
-            "indexed" : true,
-            "internalType" : "address",
-            "name" : "_operator",
-            "type" : "address"
-         },
-         {
-            "indexed" : true,
-            "internalType" : "address",
-            "name" : "_from",
-            "type" : "address"
-         },
-         {
-            "indexed" : true,
-            "internalType" : "address",
-            "name" : "_to",
-            "type" : "address"
-         },
-         {
-            "indexed" : false,
-            "internalType" : "uint256",
-            "name" : "_id",
-            "type" : "uint256"
-         },
-         {
-            "indexed" : false,
-            "internalType" : "uint256",
-            "name" : "_value",
-            "type" : "uint256"
-         }
-      ],
-      "name" : "TransferSingle",
-      "type" : "event"
-   },
-   {
-      "anonymous" : false,
-      "inputs" : [
-         {
-            "indexed" : false,
-            "internalType" : "string",
-            "name" : "_value",
-            "type" : "string"
-         },
-         {
-            "indexed" : true,
-            "internalType" : "uint256",
-            "name" : "_id",
-            "type" : "uint256"
-         }
-      ],
-      "name" : "URI",
-      "type" : "event"
-   },
-   {
-      "inputs" : [
-         {
-            "internalType" : "address",
-            "name" : "_owner",
-            "type" : "address"
-         },
-         {
-            "internalType" : "uint256",
-            "name" : "_id",
-            "type" : "uint256"
-         }
-      ],
-      "name" : "balanceOf",
-      "outputs" : [
-         {
-            "internalType" : "uint256",
-            "name" : "",
-            "type" : "uint256"
-         }
-      ],
-      "stateMutability" : "view",
-      "type" : "function"
-   },
-   {
-      "inputs" : [
-         {
-            "internalType" : "address[]",
-            "name" : "_owners",
-            "type" : "address[]"
-         },
-         {
-            "internalType" : "uint256[]",
-            "name" : "_ids",
-            "type" : "uint256[]"
-         }
-      ],
-      "name" : "balanceOfBatch",
-      "outputs" : [
-         {
-            "internalType" : "uint256[]",
-            "name" : "",
-            "type" : "uint256[]"
-         }
-      ],
-      "stateMutability" : "view",
-      "type" : "function"
-   },
-   {
-      "inputs" : [
-         {
-            "internalType" : "address",
-            "name" : "_owner",
-            "type" : "address"
-         },
-         {
-            "internalType" : "address",
-            "name" : "_operator",
-            "type" : "address"
-         }
-      ],
-      "name" : "isApprovedForAll",
-      "outputs" : [
-         {
-            "internalType" : "bool",
-            "name" : "",
-            "type" : "bool"
-         }
-      ],
-      "stateMutability" : "view",
-      "type" : "function"
-   },
-   {
-      "inputs" : [
-         {
-            "internalType" : "address",
-            "name" : "_from",
-            "type" : "address"
-         },
-         {
-            "internalType" : "address",
-            "name" : "_to",
-            "type" : "address"
-         },
-         {
-            "internalType" : "uint256[]",
-            "name" : "_ids",
-            "type" : "uint256[]"
-         },
-         {
-            "internalType" : "uint256[]",
-            "name" : "_values",
-            "type" : "uint256[]"
-         },
-         {
-            "internalType" : "bytes",
-            "name" : "_data",
-            "type" : "bytes"
-         }
-      ],
-      "name" : "safeBatchTransferFrom",
-      "outputs" : [],
-      "stateMutability" : "nonpayable",
-      "type" : "function"
-   },
-   {
-      "inputs" : [
-         {
-            "internalType" : "address",
-            "name" : "_from",
-            "type" : "address"
-         },
-         {
-            "internalType" : "address",
-            "name" : "_to",
-            "type" : "address"
-         },
-         {
-            "internalType" : "uint256",
-            "name" : "_id",
-            "type" : "uint256"
-         },
-         {
-            "internalType" : "uint256",
-            "name" : "_value",
-            "type" : "uint256"
-         },
-         {
-            "internalType" : "bytes",
-            "name" : "_data",
-            "type" : "bytes"
-         }
-      ],
-      "name" : "safeTransferFrom",
-      "outputs" : [],
-      "stateMutability" : "nonpayable",
-      "type" : "function"
-   },
-   {
-      "inputs" : [
-         {
-            "internalType" : "address",
-            "name" : "_operator",
-            "type" : "address"
-         },
-         {
-            "internalType" : "bool",
-            "name" : "_approved",
-            "type" : "bool"
-         }
-      ],
-      "name" : "setApprovalForAll",
-      "outputs" : [],
-      "stateMutability" : "nonpayable",
-      "type" : "function"
-   }
+    {
+        "anonymous" : false,
+        "inputs" : [
+            {
+                "indexed" : true,
+                "internalType" : "address",
+                "name" : "_owner",
+                "type" : "address"
+            },
+            {
+                "indexed" : true,
+                "internalType" : "address",
+                "name" : "_operator",
+                "type" : "address"
+            },
+            {
+                "indexed" : false,
+                "internalType" : "bool",
+                "name" : "_approved",
+                "type" : "bool"
+            }
+        ],
+        "name" : "ApprovalForAll",
+        "type" : "event"
+    },
+    {
+        "anonymous" : false,
+        "inputs" : [
+            {
+                "indexed" : true,
+                "internalType" : "address",
+                "name" : "_operator",
+                "type" : "address"
+            },
+            {
+                "indexed" : true,
+                "internalType" : "address",
+                "name" : "_from",
+                "type" : "address"
+            },
+            {
+                "indexed" : true,
+                "internalType" : "address",
+                "name" : "_to",
+                "type" : "address"
+            },
+            {
+                "indexed" : false,
+                "internalType" : "uint256[]",
+                "name" : "_ids",
+                "type" : "uint256[]"
+            },
+            {
+                "indexed" : false,
+                "internalType" : "uint256[]",
+                "name" : "_values",
+                "type" : "uint256[]"
+            }
+        ],
+        "name" : "TransferBatch",
+        "type" : "event"
+    },
+    {
+        "anonymous" : false,
+        "inputs" : [
+            {
+                "indexed" : true,
+                "internalType" : "address",
+                "name" : "_operator",
+                "type" : "address"
+            },
+            {
+                "indexed" : true,
+                "internalType" : "address",
+                "name" : "_from",
+                "type" : "address"
+            },
+            {
+                "indexed" : true,
+                "internalType" : "address",
+                "name" : "_to",
+                "type" : "address"
+            },
+            {
+                "indexed" : false,
+                "internalType" : "uint256",
+                "name" : "_id",
+                "type" : "uint256"
+            },
+            {
+                "indexed" : false,
+                "internalType" : "uint256",
+                "name" : "_value",
+                "type" : "uint256"
+            }
+        ],
+        "name" : "TransferSingle",
+        "type" : "event"
+    },
+    {
+        "anonymous" : false,
+        "inputs" : [
+            {
+                "indexed" : false,
+                "internalType" : "string",
+                "name" : "_value",
+                "type" : "string"
+            },
+            {
+                "indexed" : true,
+                "internalType" : "uint256",
+                "name" : "_id",
+                "type" : "uint256"
+            }
+        ],
+        "name" : "URI",
+        "type" : "event"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_owner",
+                "type" : "address"
+            },
+            {
+                "internalType" : "uint256",
+                "name" : "_id",
+                "type" : "uint256"
+            }
+        ],
+        "name" : "balanceOf",
+        "outputs" : [
+            {
+                "internalType" : "uint256",
+                "name" : "",
+                "type" : "uint256"
+            }
+        ],
+        "stateMutability" : "view",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address[]",
+                "name" : "_owners",
+                "type" : "address[]"
+            },
+            {
+                "internalType" : "uint256[]",
+                "name" : "_ids",
+                "type" : "uint256[]"
+            }
+        ],
+        "name" : "balanceOfBatch",
+        "outputs" : [
+            {
+                "internalType" : "uint256[]",
+                "name" : "",
+                "type" : "uint256[]"
+            }
+        ],
+        "stateMutability" : "view",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_owner",
+                "type" : "address"
+            },
+            {
+                "internalType" : "address",
+                "name" : "_operator",
+                "type" : "address"
+            }
+        ],
+        "name" : "isApprovedForAll",
+        "outputs" : [
+            {
+                "internalType" : "bool",
+                "name" : "",
+                "type" : "bool"
+            }
+        ],
+        "stateMutability" : "view",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_from",
+                "type" : "address"
+            },
+            {
+                "internalType" : "address",
+                "name" : "_to",
+                "type" : "address"
+            },
+            {
+                "internalType" : "uint256[]",
+                "name" : "_ids",
+                "type" : "uint256[]"
+            },
+            {
+                "internalType" : "uint256[]",
+                "name" : "_values",
+                "type" : "uint256[]"
+            },
+            {
+                "internalType" : "bytes",
+                "name" : "_data",
+                "type" : "bytes"
+            }
+        ],
+        "name" : "safeBatchTransferFrom",
+        "outputs" : [],
+        "stateMutability" : "nonpayable",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_from",
+                "type" : "address"
+            },
+            {
+                "internalType" : "address",
+                "name" : "_to",
+                "type" : "address"
+            },
+            {
+                "internalType" : "uint256",
+                "name" : "_id",
+                "type" : "uint256"
+            },
+            {
+                "internalType" : "uint256",
+                "name" : "_value",
+                "type" : "uint256"
+            },
+            {
+                "internalType" : "bytes",
+                "name" : "_data",
+                "type" : "bytes"
+            }
+        ],
+        "name" : "safeTransferFrom",
+        "outputs" : [],
+        "stateMutability" : "nonpayable",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_operator",
+                "type" : "address"
+            },
+            {
+                "internalType" : "bool",
+                "name" : "_approved",
+                "type" : "bool"
+            }
+        ],
+        "name" : "setApprovalForAll",
+        "outputs" : [],
+        "stateMutability" : "nonpayable",
+        "type" : "function"
+    }
 ]
diff --git a/tests/ragger/abis/erc20.json b/tests/ragger/abis/erc20.json
new file mode 100644
index 000000000..301b94656
--- /dev/null
+++ b/tests/ragger/abis/erc20.json
@@ -0,0 +1,135 @@
+[
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_owner",
+                "type" : "address"
+            },
+            {
+                "internalType" : "address",
+                "name" : "_spender",
+                "type" : "address"
+            }
+        ],
+        "name" : "allowance",
+        "outputs" : [
+            {
+                "internalType" : "uint256",
+                "name" : "remaining",
+                "type" : "uint256"
+            }
+        ],
+        "stateMutability" : "view",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_spender",
+                "type" : "address"
+            },
+            {
+                "internalType" : "uint256",
+                "name" : "_value",
+                "type" : "uint256"
+            }
+        ],
+        "name" : "approve",
+        "outputs" : [
+            {
+                "internalType" : "bool",
+                "name" : "success",
+                "type" : "bool"
+            }
+        ],
+        "stateMutability" : "nonpayable",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_owner",
+                "type" : "address"
+            }
+        ],
+        "name" : "balanceOf",
+        "outputs" : [
+            {
+                "internalType" : "uint256",
+                "name" : "balance",
+                "type" : "uint256"
+            }
+        ],
+        "stateMutability" : "view",
+        "type" : "function"
+    },
+    {
+        "inputs" : [],
+        "name" : "totalSupply",
+        "outputs" : [
+            {
+                "internalType" : "uint256",
+                "name" : "",
+                "type" : "uint256"
+            }
+        ],
+        "stateMutability" : "view",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_to",
+                "type" : "address"
+            },
+            {
+                "internalType" : "uint256",
+                "name" : "_value",
+                "type" : "uint256"
+            }
+        ],
+        "name" : "transfer",
+        "outputs" : [
+            {
+                "internalType" : "bool",
+                "name" : "success",
+                "type" : "bool"
+            }
+        ],
+        "stateMutability" : "nonpayable",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_from",
+                "type" : "address"
+            },
+            {
+                "internalType" : "address",
+                "name" : "_to",
+                "type" : "address"
+            },
+            {
+                "internalType" : "uint256",
+                "name" : "_value",
+                "type" : "uint256"
+            }
+        ],
+        "name" : "transferFrom",
+        "outputs" : [
+            {
+                "internalType" : "bool",
+                "name" : "success",
+                "type" : "bool"
+            }
+        ],
+        "stateMutability" : "nonpayable",
+        "type" : "function"
+    }
+]
diff --git a/tests/ragger/abis/erc721.json b/tests/ragger/abis/erc721.json
index e00d5ca3f..b6f583563 100644
--- a/tests/ragger/abis/erc721.json
+++ b/tests/ragger/abis/erc721.json
@@ -1,268 +1,268 @@
 [
-   {
-      "anonymous" : false,
-      "inputs" : [
-         {
-            "indexed" : true,
-            "internalType" : "address",
-            "name" : "_owner",
-            "type" : "address"
-         },
-         {
-            "indexed" : true,
-            "internalType" : "address",
-            "name" : "_approved",
-            "type" : "address"
-         },
-         {
-            "indexed" : true,
-            "internalType" : "uint256",
-            "name" : "_tokenId",
-            "type" : "uint256"
-         }
-      ],
-      "name" : "Approval",
-      "type" : "event"
-   },
-   {
-      "anonymous" : false,
-      "inputs" : [
-         {
-            "indexed" : true,
-            "internalType" : "address",
-            "name" : "_owner",
-            "type" : "address"
-         },
-         {
-            "indexed" : true,
-            "internalType" : "address",
-            "name" : "_operator",
-            "type" : "address"
-         },
-         {
-            "indexed" : false,
-            "internalType" : "bool",
-            "name" : "_approved",
-            "type" : "bool"
-         }
-      ],
-      "name" : "ApprovalForAll",
-      "type" : "event"
-   },
-   {
-      "anonymous" : false,
-      "inputs" : [
-         {
-            "indexed" : true,
-            "internalType" : "address",
-            "name" : "_from",
-            "type" : "address"
-         },
-         {
-            "indexed" : true,
-            "internalType" : "address",
-            "name" : "_to",
-            "type" : "address"
-         },
-         {
-            "indexed" : true,
-            "internalType" : "uint256",
-            "name" : "_tokenId",
-            "type" : "uint256"
-         }
-      ],
-      "name" : "Transfer",
-      "type" : "event"
-   },
-   {
-      "inputs" : [
-         {
-            "internalType" : "address",
-            "name" : "_approved",
-            "type" : "address"
-         },
-         {
-            "internalType" : "uint256",
-            "name" : "_tokenId",
-            "type" : "uint256"
-         }
-      ],
-      "name" : "approve",
-      "outputs" : [],
-      "stateMutability" : "payable",
-      "type" : "function"
-   },
-   {
-      "inputs" : [
-         {
-            "internalType" : "address",
-            "name" : "_owner",
-            "type" : "address"
-         }
-      ],
-      "name" : "balanceOf",
-      "outputs" : [
-         {
-            "internalType" : "uint256",
-            "name" : "",
-            "type" : "uint256"
-         }
-      ],
-      "stateMutability" : "view",
-      "type" : "function"
-   },
-   {
-      "inputs" : [
-         {
-            "internalType" : "uint256",
-            "name" : "_tokenId",
-            "type" : "uint256"
-         }
-      ],
-      "name" : "getApproved",
-      "outputs" : [
-         {
-            "internalType" : "address",
-            "name" : "",
-            "type" : "address"
-         }
-      ],
-      "stateMutability" : "view",
-      "type" : "function"
-   },
-   {
-      "inputs" : [
-         {
-            "internalType" : "address",
-            "name" : "_owner",
-            "type" : "address"
-         },
-         {
-            "internalType" : "address",
-            "name" : "_operator",
-            "type" : "address"
-         }
-      ],
-      "name" : "isApprovedForAll",
-      "outputs" : [
-         {
-            "internalType" : "bool",
-            "name" : "",
-            "type" : "bool"
-         }
-      ],
-      "stateMutability" : "view",
-      "type" : "function"
-   },
-   {
-      "inputs" : [
-         {
-            "internalType" : "uint256",
-            "name" : "_tokenId",
-            "type" : "uint256"
-         }
-      ],
-      "name" : "ownerOf",
-      "outputs" : [
-         {
-            "internalType" : "address",
-            "name" : "",
-            "type" : "address"
-         }
-      ],
-      "stateMutability" : "view",
-      "type" : "function"
-   },
-   {
-      "inputs" : [
-         {
-            "internalType" : "address",
-            "name" : "_from",
-            "type" : "address"
-         },
-         {
-            "internalType" : "address",
-            "name" : "_to",
-            "type" : "address"
-         },
-         {
-            "internalType" : "uint256",
-            "name" : "_tokenId",
-            "type" : "uint256"
-         }
-      ],
-      "name" : "safeTransferFrom",
-      "outputs" : [],
-      "stateMutability" : "payable",
-      "type" : "function"
-   },
-   {
-      "inputs" : [
-         {
-            "internalType" : "address",
-            "name" : "_from",
-            "type" : "address"
-         },
-         {
-            "internalType" : "address",
-            "name" : "_to",
-            "type" : "address"
-         },
-         {
-            "internalType" : "uint256",
-            "name" : "_tokenId",
-            "type" : "uint256"
-         },
-         {
-            "internalType" : "bytes",
-            "name" : "data",
-            "type" : "bytes"
-         }
-      ],
-      "name" : "safeTransferFrom",
-      "outputs" : [],
-      "stateMutability" : "payable",
-      "type" : "function"
-   },
-   {
-      "inputs" : [
-         {
-            "internalType" : "address",
-            "name" : "_operator",
-            "type" : "address"
-         },
-         {
-            "internalType" : "bool",
-            "name" : "_approved",
-            "type" : "bool"
-         }
-      ],
-      "name" : "setApprovalForAll",
-      "outputs" : [],
-      "stateMutability" : "nonpayable",
-      "type" : "function"
-   },
-   {
-      "inputs" : [
-         {
-            "internalType" : "address",
-            "name" : "_from",
-            "type" : "address"
-         },
-         {
-            "internalType" : "address",
-            "name" : "_to",
-            "type" : "address"
-         },
-         {
-            "internalType" : "uint256",
-            "name" : "_tokenId",
-            "type" : "uint256"
-         }
-      ],
-      "name" : "transferFrom",
-      "outputs" : [],
-      "stateMutability" : "payable",
-      "type" : "function"
-   }
+    {
+        "anonymous" : false,
+        "inputs" : [
+            {
+                "indexed" : true,
+                "internalType" : "address",
+                "name" : "_owner",
+                "type" : "address"
+            },
+            {
+                "indexed" : true,
+                "internalType" : "address",
+                "name" : "_approved",
+                "type" : "address"
+            },
+            {
+                "indexed" : true,
+                "internalType" : "uint256",
+                "name" : "_tokenId",
+                "type" : "uint256"
+            }
+        ],
+        "name" : "Approval",
+        "type" : "event"
+    },
+    {
+        "anonymous" : false,
+        "inputs" : [
+            {
+                "indexed" : true,
+                "internalType" : "address",
+                "name" : "_owner",
+                "type" : "address"
+            },
+            {
+                "indexed" : true,
+                "internalType" : "address",
+                "name" : "_operator",
+                "type" : "address"
+            },
+            {
+                "indexed" : false,
+                "internalType" : "bool",
+                "name" : "_approved",
+                "type" : "bool"
+            }
+        ],
+        "name" : "ApprovalForAll",
+        "type" : "event"
+    },
+    {
+        "anonymous" : false,
+        "inputs" : [
+            {
+                "indexed" : true,
+                "internalType" : "address",
+                "name" : "_from",
+                "type" : "address"
+            },
+            {
+                "indexed" : true,
+                "internalType" : "address",
+                "name" : "_to",
+                "type" : "address"
+            },
+            {
+                "indexed" : true,
+                "internalType" : "uint256",
+                "name" : "_tokenId",
+                "type" : "uint256"
+            }
+        ],
+        "name" : "Transfer",
+        "type" : "event"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_approved",
+                "type" : "address"
+            },
+            {
+                "internalType" : "uint256",
+                "name" : "_tokenId",
+                "type" : "uint256"
+            }
+        ],
+        "name" : "approve",
+        "outputs" : [],
+        "stateMutability" : "payable",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_owner",
+                "type" : "address"
+            }
+        ],
+        "name" : "balanceOf",
+        "outputs" : [
+            {
+                "internalType" : "uint256",
+                "name" : "",
+                "type" : "uint256"
+            }
+        ],
+        "stateMutability" : "view",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "uint256",
+                "name" : "_tokenId",
+                "type" : "uint256"
+            }
+        ],
+        "name" : "getApproved",
+        "outputs" : [
+            {
+                "internalType" : "address",
+                "name" : "",
+                "type" : "address"
+            }
+        ],
+        "stateMutability" : "view",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_owner",
+                "type" : "address"
+            },
+            {
+                "internalType" : "address",
+                "name" : "_operator",
+                "type" : "address"
+            }
+        ],
+        "name" : "isApprovedForAll",
+        "outputs" : [
+            {
+                "internalType" : "bool",
+                "name" : "",
+                "type" : "bool"
+            }
+        ],
+        "stateMutability" : "view",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "uint256",
+                "name" : "_tokenId",
+                "type" : "uint256"
+            }
+        ],
+        "name" : "ownerOf",
+        "outputs" : [
+            {
+                "internalType" : "address",
+                "name" : "",
+                "type" : "address"
+            }
+        ],
+        "stateMutability" : "view",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_from",
+                "type" : "address"
+            },
+            {
+                "internalType" : "address",
+                "name" : "_to",
+                "type" : "address"
+            },
+            {
+                "internalType" : "uint256",
+                "name" : "_tokenId",
+                "type" : "uint256"
+            }
+        ],
+        "name" : "safeTransferFrom",
+        "outputs" : [],
+        "stateMutability" : "payable",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_from",
+                "type" : "address"
+            },
+            {
+                "internalType" : "address",
+                "name" : "_to",
+                "type" : "address"
+            },
+            {
+                "internalType" : "uint256",
+                "name" : "_tokenId",
+                "type" : "uint256"
+            },
+            {
+                "internalType" : "bytes",
+                "name" : "data",
+                "type" : "bytes"
+            }
+        ],
+        "name" : "safeTransferFrom",
+        "outputs" : [],
+        "stateMutability" : "payable",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_operator",
+                "type" : "address"
+            },
+            {
+                "internalType" : "bool",
+                "name" : "_approved",
+                "type" : "bool"
+            }
+        ],
+        "name" : "setApprovalForAll",
+        "outputs" : [],
+        "stateMutability" : "nonpayable",
+        "type" : "function"
+    },
+    {
+        "inputs" : [
+            {
+                "internalType" : "address",
+                "name" : "_from",
+                "type" : "address"
+            },
+            {
+                "internalType" : "address",
+                "name" : "_to",
+                "type" : "address"
+            },
+            {
+                "internalType" : "uint256",
+                "name" : "_tokenId",
+                "type" : "uint256"
+            }
+        ],
+        "name" : "transferFrom",
+        "outputs" : [],
+        "stateMutability" : "payable",
+        "type" : "function"
+    }
 ]
diff --git a/tests/ragger/constants.py b/tests/ragger/constants.py
index 86b6304ee..0f375a71f 100644
--- a/tests/ragger/constants.py
+++ b/tests/ragger/constants.py
@@ -1,3 +1,5 @@
 from pathlib import Path
+import os
 
 ROOT_SNAPSHOT_PATH = Path(__file__).parent
+ABIS_FOLDER = "%s/abis" % (os.path.dirname(__file__))
diff --git a/tests/ragger/snapshots/nanos/blind-signed_approval/00000.png b/tests/ragger/snapshots/nanos/blind-signed_approval/00000.png
new file mode 100644
index 000000000..c2420afe1
Binary files /dev/null and b/tests/ragger/snapshots/nanos/blind-signed_approval/00000.png differ
diff --git a/tests/ragger/snapshots/nanos/blind-signed_approval/00001.png b/tests/ragger/snapshots/nanos/blind-signed_approval/00001.png
new file mode 100644
index 000000000..b31e45596
Binary files /dev/null and b/tests/ragger/snapshots/nanos/blind-signed_approval/00001.png differ
diff --git a/tests/ragger/snapshots/nanos/blind-signed_approval/00002.png b/tests/ragger/snapshots/nanos/blind-signed_approval/00002.png
new file mode 100644
index 000000000..ce795f34e
Binary files /dev/null and b/tests/ragger/snapshots/nanos/blind-signed_approval/00002.png differ
diff --git a/tests/ragger/snapshots/nanosp/blind-signed_approval/00000.png b/tests/ragger/snapshots/nanosp/blind-signed_approval/00000.png
new file mode 100644
index 000000000..11ea8a56b
Binary files /dev/null and b/tests/ragger/snapshots/nanosp/blind-signed_approval/00000.png differ
diff --git a/tests/ragger/snapshots/nanosp/blind-signed_approval/00001.png b/tests/ragger/snapshots/nanosp/blind-signed_approval/00001.png
new file mode 100644
index 000000000..657887225
Binary files /dev/null and b/tests/ragger/snapshots/nanosp/blind-signed_approval/00001.png differ
diff --git a/tests/ragger/snapshots/nanox/blind-signed_approval/00000.png b/tests/ragger/snapshots/nanox/blind-signed_approval/00000.png
new file mode 100644
index 000000000..11ea8a56b
Binary files /dev/null and b/tests/ragger/snapshots/nanox/blind-signed_approval/00000.png differ
diff --git a/tests/ragger/snapshots/nanox/blind-signed_approval/00001.png b/tests/ragger/snapshots/nanox/blind-signed_approval/00001.png
new file mode 100644
index 000000000..657887225
Binary files /dev/null and b/tests/ragger/snapshots/nanox/blind-signed_approval/00001.png differ
diff --git a/tests/ragger/snapshots/stax/blind-signed_approval/00000.png b/tests/ragger/snapshots/stax/blind-signed_approval/00000.png
new file mode 100644
index 000000000..7c13332b6
Binary files /dev/null and b/tests/ragger/snapshots/stax/blind-signed_approval/00000.png differ
diff --git a/tests/ragger/snapshots/stax/blind-signed_approval/00001.png b/tests/ragger/snapshots/stax/blind-signed_approval/00001.png
new file mode 100644
index 000000000..b1ff1b3ab
Binary files /dev/null and b/tests/ragger/snapshots/stax/blind-signed_approval/00001.png differ
diff --git a/tests/ragger/test_blind_sign.py b/tests/ragger/test_blind_sign.py
new file mode 100644
index 000000000..a4ed7679c
--- /dev/null
+++ b/tests/ragger/test_blind_sign.py
@@ -0,0 +1,55 @@
+import json
+from ragger.backend import BackendInterface
+from ragger.firmware import Firmware
+from ragger.navigator import Navigator, NavInsID
+from ragger.error import ExceptionRAPDU
+from ledger_app_clients.ethereum.client import EthAppClient
+from web3 import Web3
+from constants import ROOT_SNAPSHOT_PATH, ABIS_FOLDER
+
+
+# Token approval, would require loading the "internal plugin" &
+# providing the token metadata from the CAL
+def test_blind_sign(firmware: Firmware,
+                    backend: BackendInterface,
+                    navigator: Navigator):
+    app_client = EthAppClient(backend)
+
+    with open("%s/erc20.json" % (ABIS_FOLDER)) as file:
+        contract = Web3().eth.contract(
+            abi=json.load(file),
+            address=None
+        )
+    data = contract.encodeABI("approve", [
+        # Uniswap Protocol: Permit2
+        bytes.fromhex("000000000022d473030f116ddee9f6b43ac78ba3"),
+        Web3.to_wei("2", "ether")
+    ])
+    tx_params = {
+        "nonce": 235,
+        "maxFeePerGas": Web3.to_wei(100, "gwei"),
+        "maxPriorityFeePerGas": Web3.to_wei(10, "gwei"),
+        "gas": 44001,
+        # Maker: Dai Stablecoin
+        "to": bytes.fromhex("6b175474e89094c44da98b954eedeac495271d0f"),
+        "data": data,
+        "chainId": 1
+    }
+    try:
+        with app_client.sign("m/44'/60'/0'/0/0", tx_params):
+            pass
+    except ExceptionRAPDU:
+        pass
+    else:
+        assert False
+
+    moves = list()
+    if firmware.device.startswith("nano"):
+        if firmware.device == "nanos":
+            moves += [NavInsID.RIGHT_CLICK]
+        moves += [NavInsID.BOTH_CLICK]
+    else:
+        moves += [NavInsID.USE_CASE_CHOICE_CONFIRM]
+    navigator.navigate_and_compare(ROOT_SNAPSHOT_PATH,
+                                   "blind-signed_approval",
+                                   moves)
diff --git a/tests/ragger/test_nft.py b/tests/ragger/test_nft.py
index 87c1ac0c4..fd3987122 100644
--- a/tests/ragger/test_nft.py
+++ b/tests/ragger/test_nft.py
@@ -11,12 +11,9 @@
 from ledger_app_clients.ethereum.utils import get_selector_from_data, recover_transaction
 from web3 import Web3
 import json
-import os
-from constants import ROOT_SNAPSHOT_PATH
+from constants import ROOT_SNAPSHOT_PATH, ABIS_FOLDER
 
 
-ABIS_FOLDER = "%s/abis" % (os.path.dirname(__file__))
-
 BIP32_PATH = "m/44'/60'/0'/0/0"
 NONCE = 21
 GAS_PRICE = 13
diff --git a/tests/ragger/test_sign.py b/tests/ragger/test_sign.py
new file mode 100644
index 000000000..401be19a0
--- /dev/null
+++ b/tests/ragger/test_sign.py
@@ -0,0 +1,67 @@
+from ragger.backend import BackendInterface
+from ragger.firmware import Firmware
+from ragger.navigator import Navigator, NavInsID
+from ledger_app_clients.ethereum.client import EthAppClient
+import ledger_app_clients.ethereum.response_parser as ResponseParser
+from ledger_app_clients.ethereum.utils import recover_transaction
+from web3 import Web3
+
+
+# Values used across all tests
+CHAIN_ID = 1
+ADDR = bytes.fromhex("0011223344556677889900112233445566778899")
+BIP32_PATH = "m/44'/60'/0'/0/0"
+NONCE = 21
+GAS_PRICE = 13
+GAS_LIMIT = 21000
+AMOUNT = 1.22
+
+
+def common(fw: Firmware,
+           back: BackendInterface,
+           nav: Navigator,
+           tx_params: dict):
+    app_client = EthAppClient(back)
+
+    with app_client.get_public_addr(display=False):
+        pass
+    _, DEVICE_ADDR, _ = ResponseParser.pk_addr(app_client.response().data)
+
+    with app_client.sign(BIP32_PATH, tx_params):
+        if fw.device.startswith("nano"):
+            next_action = NavInsID.RIGHT_CLICK
+            confirm_action = NavInsID.BOTH_CLICK
+            end_text = "Accept"
+        else:
+            next_action = NavInsID.USE_CASE_REVIEW_TAP
+            confirm_action = NavInsID.USE_CASE_REVIEW_CONFIRM
+            end_text = "Sign"
+        nav.navigate_until_text(next_action, [confirm_action], end_text)
+
+    # verify signature
+    vrs = ResponseParser.signature(app_client.response().data)
+    addr = recover_transaction(tx_params, vrs)
+    assert addr == DEVICE_ADDR
+
+
+def test_legacy(firmware: Firmware, backend: BackendInterface, navigator: Navigator):
+    common(firmware, backend, navigator, {
+        "nonce": NONCE,
+        "gasPrice": Web3.to_wei(GAS_PRICE, "gwei"),
+        "gas": GAS_LIMIT,
+        "to": ADDR,
+        "value": Web3.to_wei(AMOUNT, "ether"),
+        "chainId": CHAIN_ID
+    })
+
+
+def test_1559(firmware: Firmware, backend: BackendInterface, navigator: Navigator):
+    common(firmware, backend, navigator, {
+        "nonce": NONCE,
+        "maxFeePerGas": Web3.to_wei(145, "gwei"),
+        "maxPriorityFeePerGas": Web3.to_wei(1.5, "gwei"),
+        "gas": GAS_LIMIT,
+        "to": ADDR,
+        "value": Web3.to_wei(AMOUNT, "ether"),
+        "chainId": CHAIN_ID
+    })