mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-06-28 19:02:34 +00:00
feat(core): introduce a flow for ethereum approve
This commit is contained in:
parent
c275b5d995
commit
0ba8173424
199
common/tests/fixtures/ethereum/sign_tx_erc20.json
vendored
Normal file
199
common/tests/fixtures/ethereum/sign_tx_erc20.json
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
{
|
||||
"setup": {
|
||||
"mnemonic": "alcohol woman abuse must during monitor noble actual mixed trade anger aisle",
|
||||
"passphrase": ""
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"name": "approve_known_token_known_chain",
|
||||
"parameters": {
|
||||
"comment": "Approve",
|
||||
"data": "095ea7b3000000000000000000000000574bbb36871ba6b78e27f4b4dcfb76ea0091880b0000000000000000000000000000000000000000000000000000000000000064",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 37,
|
||||
"sig_r": "3be4aa3820a4afd92664f1d6811610c7205fcb34da3dbacc881f514b04d46276",
|
||||
"sig_s": "7367fa66ebf7cb0f8497b035d9de6e035cd9c24f04a19b106c62b1cfb96393ce"
|
||||
},
|
||||
"skip_models": ["t1"]
|
||||
},
|
||||
{
|
||||
"name": "revoke_known_token_known_chain",
|
||||
"parameters": {
|
||||
"comment": "Revoke",
|
||||
"data": "095ea7b3000000000000000000000000574bbb36871ba6b78e27f4b4dcfb76ea0091880b0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 37,
|
||||
"sig_r": "eb4a6eb73e657fb4bd9e622f7af7cc49019fdac457a59376d596b86ec1cf8104",
|
||||
"sig_s": "0cf53c99917d603635686898035eb9f2611ccc0c388e89ec02ec640a9e0e30eb"
|
||||
},
|
||||
"skip_models": ["t1"]
|
||||
},
|
||||
{
|
||||
"name": "approve_uniswap_unknown_token",
|
||||
"parameters": {
|
||||
"comment": "Approve - Uniswap - unknown token",
|
||||
"data": "095ea7b3000000000000000000000000e592427a0aece92de3edee1f18e0157c058615640000000000000000000000000000000000000000000000000000000000000064",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xfc6b5d6af8a13258f7cbd0d39e11b35e01a32f93",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 37,
|
||||
"sig_r": "2854b6fd729e66f2fde19da60442612ae775aa3950cf2b7702a925b584b9e520",
|
||||
"sig_s": "1dae1dfe009ba01edb3b96d61490c2eeea12dc20ef71c31fb846c5b60283310a"
|
||||
},
|
||||
"skip_models": ["t1"]
|
||||
},
|
||||
{
|
||||
"name": "revoke_uniswap_unknown_token",
|
||||
"parameters": {
|
||||
"comment": "Revoke - Uniswap - unknown token",
|
||||
"data": "095ea7b3000000000000000000000000e592427a0aece92de3edee1f18e0157c058615640000000000000000000000000000000000000000000000000000000000000000",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xfc6b5d6af8a13258f7cbd0d39e11b35e01a32f93",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x00"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 37,
|
||||
"sig_r": "461c9aaa41b851bf64157521d5aa4f367f5af27db243de52545247184a594d24",
|
||||
"sig_s": "45a6cb062a304f06c4a56e5357ad50381283433d65ea687a7c1f50a221267c4a"
|
||||
},
|
||||
"skip_models": ["t1"]
|
||||
},
|
||||
{
|
||||
"name": "approve_unlimited",
|
||||
"parameters": {
|
||||
"comment": "Approve - unlimited",
|
||||
"data": "095ea7b3000000000000000000000000574bbb36871ba6b78e27f4b4dcfb76ea0091880bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xfc6b5d6af8a13258f7cbd0d39e11b35e01a32f93",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 38,
|
||||
"sig_r": "34b03573f2436ec3a90f0dc4f0ebe1f2f850cdad1baf17ff6d762210ab0d0e37",
|
||||
"sig_s": "5906821df079dfb4fb515e64582eda5ca6307e35074f059a7abf8c7a1917362a"
|
||||
},
|
||||
"skip_models": ["t1"]
|
||||
},
|
||||
{
|
||||
"name": "approve_unknown_token_unknown_chain",
|
||||
"parameters": {
|
||||
"comment": "Approve - unknown token unknown chain",
|
||||
"data": "095ea7b3000000000000000000000000574bbb36871ba6b78e27f4b4dcfb76ea0091880b0000000000000000000000000000000000000000000000000000000000000064",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xdddddddddddddddddddddddddddddddddddddddd",
|
||||
"chain_id": 200901,
|
||||
"fake_defs": false,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 401837,
|
||||
"sig_r": "cf8cb9bd0aa970617c989108e80ee222f5d0af74cef4baa952c63584c8fe713a",
|
||||
"sig_s": "08253cf6904c7c6d78ff19999bce746a0d97396c196228ee67dbf4e3e8cc3798"
|
||||
},
|
||||
"skip_models": ["t1"]
|
||||
},
|
||||
{
|
||||
"name": "revoke_unknown_token_unknown_chain",
|
||||
"parameters": {
|
||||
"comment": "Revoke - unknown token unknown chain",
|
||||
"data": "095ea7b3000000000000000000000000574bbb36871ba6b78e27f4b4dcfb76ea0091880b0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xdddddddddddddddddddddddddddddddddddddddd",
|
||||
"chain_id": 200901,
|
||||
"fake_defs": false,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 401838,
|
||||
"sig_r": "976c17f8833b8260682ab7b3cfec810f3f9239d53baa0ed622a8cccb2ba9d018",
|
||||
"sig_s": "3e1072f823e90a029b48a5aaa57630c4b1e9b6808c3af441c8abcb16b5823ca5"
|
||||
},
|
||||
"skip_models": ["t1"]
|
||||
},
|
||||
{
|
||||
"name": "approve_unknown_token_known_chain",
|
||||
"parameters": {
|
||||
"comment": "Approve - unknown token known chain",
|
||||
"data": "095ea7b3000000000000000000000000574bbb36871ba6b78e27f4b4dcfb76ea0091880b0000000000000000000000000000000000000000000000000000000000000064",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xfc6b5d6af8a13258f7cbd0d39e11b35e01a32f93",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 37,
|
||||
"sig_r": "b10bb751b48d72bbb6d55ecb34800c08f92fda818480f67190d923c5bd73d2c4",
|
||||
"sig_s": "78fb7fa237bc6bfa0e5bdb96164db4162f41866f2f30b54295d947fe87a813a8"
|
||||
},
|
||||
"skip_models": ["t1"]
|
||||
},
|
||||
{
|
||||
"name": "revoke_unknown_token_known_chain",
|
||||
"parameters": {
|
||||
"comment": "Revoke - unknown token known chain",
|
||||
"data": "095ea7b3000000000000000000000000574bbb36871ba6b78e27f4b4dcfb76ea0091880b0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xfc6b5d6af8a13258f7cbd0d39e11b35e01a32f93",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 37,
|
||||
"sig_r": "21393dee0befb33c52b4916019df6b1bbace9015221e8f32de82f2554a8fad2d",
|
||||
"sig_s": "42f03f5dd99ed5b2368e85048546468fd9ae962bb0c7b6650bf0a39b9b2bb517"
|
||||
},
|
||||
"skip_models": ["t1"]
|
||||
}
|
||||
]
|
||||
}
|
1
core/.changelog.d/4542.added
Normal file
1
core/.changelog.d/4542.added
Normal file
@ -0,0 +1 @@
|
||||
Ethereum "approve" flow.
|
@ -786,6 +786,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_words__blockhash;
|
||||
MP_QSTR_words__buying;
|
||||
MP_QSTR_words__cancel_and_exit;
|
||||
MP_QSTR_words__chain;
|
||||
MP_QSTR_words__confirm;
|
||||
MP_QSTR_words__confirm_fee;
|
||||
MP_QSTR_words__contains;
|
||||
@ -822,8 +823,10 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_words__title_success;
|
||||
MP_QSTR_words__title_summary;
|
||||
MP_QSTR_words__title_threshold;
|
||||
MP_QSTR_words__token;
|
||||
MP_QSTR_words__try_again;
|
||||
MP_QSTR_words__unknown;
|
||||
MP_QSTR_words__unlimited;
|
||||
MP_QSTR_words__unlocked;
|
||||
MP_QSTR_words__warning;
|
||||
MP_QSTR_words__writable;
|
||||
@ -986,6 +989,17 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_eos__vote_for_proxy;
|
||||
MP_QSTR_eos__voter;
|
||||
MP_QSTR_ethereum__amount_sent;
|
||||
MP_QSTR_ethereum__approve;
|
||||
MP_QSTR_ethereum__approve_amount_allowance;
|
||||
MP_QSTR_ethereum__approve_chain_id;
|
||||
MP_QSTR_ethereum__approve_intro;
|
||||
MP_QSTR_ethereum__approve_intro_revoke;
|
||||
MP_QSTR_ethereum__approve_intro_title;
|
||||
MP_QSTR_ethereum__approve_intro_title_revoke;
|
||||
MP_QSTR_ethereum__approve_revoke;
|
||||
MP_QSTR_ethereum__approve_revoke_from;
|
||||
MP_QSTR_ethereum__approve_to;
|
||||
MP_QSTR_ethereum__approve_unlimited_template;
|
||||
MP_QSTR_ethereum__contract;
|
||||
MP_QSTR_ethereum__data_size_template;
|
||||
MP_QSTR_ethereum__gas_limit;
|
||||
@ -1143,7 +1157,6 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_solana__stake_question;
|
||||
MP_QSTR_solana__stake_withdrawal_warning;
|
||||
MP_QSTR_solana__stake_withdrawal_warning_title;
|
||||
MP_QSTR_solana__title_token;
|
||||
MP_QSTR_solana__transaction_contains_unknown_instructions;
|
||||
MP_QSTR_solana__transaction_fee;
|
||||
MP_QSTR_solana__transaction_requires_x_signers_template;
|
||||
|
@ -943,8 +943,6 @@ pub enum TranslatedString {
|
||||
#[cfg(feature = "universal_fw")]
|
||||
solana__multiple_signers = 672, // "Multiple signers"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
solana__title_token = 673, // "Token"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
solana__transaction_contains_unknown_instructions = 674, // "Transaction contains unknown instructions."
|
||||
#[cfg(feature = "universal_fw")]
|
||||
solana__transaction_requires_x_signers_template = 675, // "Transaction requires {0} signers which increases the fee."
|
||||
@ -1392,6 +1390,31 @@ pub enum TranslatedString {
|
||||
solana__max_rent_fee = 998, // "Max rent fee"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
solana__transaction_fee = 999, // "Transaction fee"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
ethereum__approve = 1000, // "Approve"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
ethereum__approve_amount_allowance = 1001, // "Amount allowance"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
ethereum__approve_chain_id = 1002, // "Chain ID"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
ethereum__approve_intro = 1003, // "Review details to approve token spending."
|
||||
#[cfg(feature = "universal_fw")]
|
||||
ethereum__approve_intro_title = 1004, // "Token approval"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
ethereum__approve_to = 1005, // "Approve to"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
ethereum__approve_unlimited_template = 1006, // "Approving unlimited amount of {0}"
|
||||
words__unlimited = 1007, // "Unlimited"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
ethereum__approve_intro_revoke = 1008, // "Review details to revoke token approval."
|
||||
#[cfg(feature = "universal_fw")]
|
||||
ethereum__approve_intro_title_revoke = 1009, // "Token revocation"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
ethereum__approve_revoke = 1010, // "Revoke"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
ethereum__approve_revoke_from = 1011, // "Revoke from"
|
||||
words__chain = 1012, // "Chain"
|
||||
words__token = 1013, // "Token"
|
||||
}
|
||||
|
||||
impl TranslatedString {
|
||||
@ -2328,8 +2351,6 @@ impl TranslatedString {
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Self::solana__multiple_signers, "Multiple signers"),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Self::solana__title_token, "Token"),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Self::solana__transaction_contains_unknown_instructions, "Transaction contains unknown instructions."),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Self::solana__transaction_requires_x_signers_template, "Transaction requires {0} signers which increases the fee."),
|
||||
@ -2777,6 +2798,31 @@ impl TranslatedString {
|
||||
(Self::solana__max_rent_fee, "Max rent fee"),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Self::solana__transaction_fee, "Transaction fee"),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Self::ethereum__approve, "Approve"),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Self::ethereum__approve_amount_allowance, "Amount allowance"),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Self::ethereum__approve_chain_id, "Chain ID"),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Self::ethereum__approve_intro, "Review details to approve token spending."),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Self::ethereum__approve_intro_title, "Token approval"),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Self::ethereum__approve_to, "Approve to"),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Self::ethereum__approve_unlimited_template, "Approving unlimited amount of {0}"),
|
||||
(Self::words__unlimited, "Unlimited"),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Self::ethereum__approve_intro_revoke, "Review details to revoke token approval."),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Self::ethereum__approve_intro_title_revoke, "Token revocation"),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Self::ethereum__approve_revoke, "Revoke"),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Self::ethereum__approve_revoke_from, "Revoke from"),
|
||||
(Self::words__chain, "Chain"),
|
||||
(Self::words__token, "Token"),
|
||||
];
|
||||
|
||||
#[cfg(feature = "micropython")]
|
||||
@ -3222,6 +3268,28 @@ impl TranslatedString {
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_ethereum__amount_sent, Self::ethereum__amount_sent),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_ethereum__approve, Self::ethereum__approve),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_ethereum__approve_amount_allowance, Self::ethereum__approve_amount_allowance),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_ethereum__approve_chain_id, Self::ethereum__approve_chain_id),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_ethereum__approve_intro, Self::ethereum__approve_intro),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_ethereum__approve_intro_revoke, Self::ethereum__approve_intro_revoke),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_ethereum__approve_intro_title, Self::ethereum__approve_intro_title),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_ethereum__approve_intro_title_revoke, Self::ethereum__approve_intro_title_revoke),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_ethereum__approve_revoke, Self::ethereum__approve_revoke),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_ethereum__approve_revoke_from, Self::ethereum__approve_revoke_from),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_ethereum__approve_to, Self::ethereum__approve_to),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_ethereum__approve_unlimited_template, Self::ethereum__approve_unlimited_template),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_ethereum__contract, Self::ethereum__contract),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_ethereum__data_size_template, Self::ethereum__data_size_template),
|
||||
@ -3878,8 +3946,6 @@ impl TranslatedString {
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_solana__stake_withdrawal_warning_title, Self::solana__stake_withdrawal_warning_title),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_solana__title_token, Self::solana__title_token),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_solana__transaction_contains_unknown_instructions, Self::solana__transaction_contains_unknown_instructions),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
(Qstr::MP_QSTR_solana__transaction_fee, Self::solana__transaction_fee),
|
||||
@ -4120,6 +4186,7 @@ impl TranslatedString {
|
||||
(Qstr::MP_QSTR_words__blockhash, Self::words__blockhash),
|
||||
(Qstr::MP_QSTR_words__buying, Self::words__buying),
|
||||
(Qstr::MP_QSTR_words__cancel_and_exit, Self::words__cancel_and_exit),
|
||||
(Qstr::MP_QSTR_words__chain, Self::words__chain),
|
||||
(Qstr::MP_QSTR_words__confirm, Self::words__confirm),
|
||||
(Qstr::MP_QSTR_words__confirm_fee, Self::words__confirm_fee),
|
||||
(Qstr::MP_QSTR_words__contains, Self::words__contains),
|
||||
@ -4156,8 +4223,10 @@ impl TranslatedString {
|
||||
(Qstr::MP_QSTR_words__title_success, Self::words__title_success),
|
||||
(Qstr::MP_QSTR_words__title_summary, Self::words__title_summary),
|
||||
(Qstr::MP_QSTR_words__title_threshold, Self::words__title_threshold),
|
||||
(Qstr::MP_QSTR_words__token, Self::words__token),
|
||||
(Qstr::MP_QSTR_words__try_again, Self::words__try_again),
|
||||
(Qstr::MP_QSTR_words__unknown, Self::words__unknown),
|
||||
(Qstr::MP_QSTR_words__unlimited, Self::words__unlimited),
|
||||
(Qstr::MP_QSTR_words__unlocked, Self::words__unlocked),
|
||||
(Qstr::MP_QSTR_words__warning, Self::words__warning),
|
||||
(Qstr::MP_QSTR_words__writable, Self::words__writable),
|
||||
|
@ -339,8 +339,14 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs:
|
||||
|
||||
extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let amount: TString = kwargs.get(Qstr::MP_QSTR_amount)?.try_into()?;
|
||||
let amount_label: TString = kwargs.get(Qstr::MP_QSTR_amount_label)?.try_into()?;
|
||||
let amount: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_amount)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let amount_label: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_amount_label)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let fee: TString = kwargs.get(Qstr::MP_QSTR_fee)?.try_into()?;
|
||||
let fee_label: TString = kwargs.get(Qstr::MP_QSTR_fee_label)?.try_into()?;
|
||||
let title: Option<TString> = kwargs
|
||||
@ -351,6 +357,10 @@ extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
.get(Qstr::MP_QSTR_account_items)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let account_title: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_account_title)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let extra_items: Option<Obj> = kwargs
|
||||
.get(Qstr::MP_QSTR_extra_items)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
@ -371,6 +381,7 @@ extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
fee_label,
|
||||
title,
|
||||
account_items,
|
||||
account_title,
|
||||
extra_items,
|
||||
extra_title,
|
||||
verb_cancel,
|
||||
@ -1315,12 +1326,13 @@ pub static mp_module_trezorui_api: Module = obj_module! {
|
||||
|
||||
/// def confirm_summary(
|
||||
/// *,
|
||||
/// amount: str,
|
||||
/// amount_label: str,
|
||||
/// amount: str | None,
|
||||
/// amount_label: str | None,
|
||||
/// fee: str,
|
||||
/// fee_label: str,
|
||||
/// title: str | None = None,
|
||||
/// account_items: Iterable[tuple[str, str]] | None = None,
|
||||
/// account_title: str | None = None,
|
||||
/// extra_items: Iterable[tuple[str, str]] | None = None,
|
||||
/// extra_title: str | None = None,
|
||||
/// verb_cancel: str | None = None,
|
||||
|
@ -427,23 +427,29 @@ impl FirmwareUI for UIBolt {
|
||||
}
|
||||
|
||||
fn confirm_summary(
|
||||
amount: TString<'static>,
|
||||
amount_label: TString<'static>,
|
||||
amount: Option<TString<'static>>,
|
||||
amount_label: Option<TString<'static>>,
|
||||
fee: TString<'static>,
|
||||
fee_label: TString<'static>,
|
||||
title: Option<TString<'static>>,
|
||||
account_items: Option<Obj>,
|
||||
_account_title: Option<TString<'static>>,
|
||||
extra_items: Option<Obj>,
|
||||
_extra_title: Option<TString<'static>>,
|
||||
verb_cancel: Option<TString<'static>>,
|
||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||
let info_button: bool = account_items.is_some() || extra_items.is_some();
|
||||
let paragraphs = ParagraphVecShort::from_iter([
|
||||
Paragraph::new(&theme::TEXT_NORMAL, amount_label).no_break(),
|
||||
Paragraph::new(&theme::TEXT_MONO, amount),
|
||||
Paragraph::new(&theme::TEXT_NORMAL, fee_label).no_break(),
|
||||
Paragraph::new(&theme::TEXT_MONO, fee),
|
||||
]);
|
||||
let mut paragraphs = ParagraphVecShort::new();
|
||||
if let Some(amount) = amount {
|
||||
if let Some(amount_label) = amount_label {
|
||||
paragraphs
|
||||
.add(Paragraph::new(&theme::TEXT_NORMAL, amount_label).no_break())
|
||||
.add(Paragraph::new(&theme::TEXT_MONO, amount));
|
||||
}
|
||||
}
|
||||
paragraphs
|
||||
.add(Paragraph::new(&theme::TEXT_NORMAL, fee_label).no_break())
|
||||
.add(Paragraph::new(&theme::TEXT_MONO, fee));
|
||||
|
||||
let mut page = ButtonPage::new(paragraphs.into_paragraphs(), theme::BG)
|
||||
.with_hold()?
|
||||
|
@ -484,12 +484,13 @@ impl FirmwareUI for UICaesar {
|
||||
}
|
||||
|
||||
fn confirm_summary(
|
||||
amount: TString<'static>,
|
||||
amount_label: TString<'static>,
|
||||
amount: Option<TString<'static>>,
|
||||
amount_label: Option<TString<'static>>,
|
||||
fee: TString<'static>,
|
||||
fee_label: TString<'static>,
|
||||
title: Option<TString<'static>>,
|
||||
account_items: Option<Obj>,
|
||||
account_title: Option<TString<'static>>,
|
||||
extra_items: Option<Obj>,
|
||||
extra_title: Option<TString<'static>>,
|
||||
verb_cancel: Option<TString<'static>>,
|
||||
@ -502,7 +503,9 @@ impl FirmwareUI for UICaesar {
|
||||
unwrap!(info_pages.push((extra_title, info)));
|
||||
}
|
||||
if let Some(info) = account_items {
|
||||
unwrap!(info_pages.push((TR::confirm_total__title_sending_from.into(), info)));
|
||||
let account_title =
|
||||
account_title.unwrap_or(TR::confirm_total__title_sending_from.into());
|
||||
unwrap!(info_pages.push((account_title, info)));
|
||||
}
|
||||
|
||||
// button layouts and actions
|
||||
@ -553,14 +556,23 @@ impl FirmwareUI for UICaesar {
|
||||
if let Some(title) = title {
|
||||
ops = ops.text(title, fonts::FONT_BOLD_UPPER).newline();
|
||||
}
|
||||
ops = ops
|
||||
.text(amount_label, fonts::FONT_BOLD)
|
||||
.newline()
|
||||
.text(amount, fonts::FONT_MONO);
|
||||
|
||||
let mut has_amount = false;
|
||||
if let Some(amount) = amount {
|
||||
if let Some(amount_label) = amount_label {
|
||||
has_amount = true;
|
||||
ops = ops
|
||||
.text(amount_label, fonts::FONT_BOLD)
|
||||
.newline()
|
||||
.text(amount, fonts::FONT_MONO);
|
||||
}
|
||||
}
|
||||
|
||||
if !fee_label.is_empty() || !fee.is_empty() {
|
||||
if has_amount {
|
||||
ops = ops.newline();
|
||||
}
|
||||
ops = ops
|
||||
.newline()
|
||||
.newline()
|
||||
.text(fee_label, fonts::FONT_BOLD)
|
||||
.newline()
|
||||
@ -1298,10 +1310,14 @@ fn content_in_button_page<T: Component + Paginate + MaybeTrace + 'static>(
|
||||
// Left button - icon, text or nothing.
|
||||
let cancel_btn = verb_cancel.map(ButtonDetails::from_text_possible_icon);
|
||||
|
||||
// Right button - text or nothing.
|
||||
// Right button - down arrow, text or nothing.
|
||||
// Optional HoldToConfirm
|
||||
let mut confirm_btn = if !verb.is_empty() {
|
||||
Some(ButtonDetails::text(verb))
|
||||
if verb == TString::Str("V") {
|
||||
Some(ButtonDetails::down_arrow_icon_wide())
|
||||
} else {
|
||||
Some(ButtonDetails::text(verb))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -358,12 +358,13 @@ impl FirmwareUI for UIDelizia {
|
||||
}
|
||||
|
||||
fn confirm_summary(
|
||||
amount: TString<'static>,
|
||||
amount_label: TString<'static>,
|
||||
amount: Option<TString<'static>>,
|
||||
amount_label: Option<TString<'static>>,
|
||||
fee: TString<'static>,
|
||||
fee_label: TString<'static>,
|
||||
title: Option<TString<'static>>,
|
||||
account_items: Option<Obj>,
|
||||
account_title: Option<TString<'static>>,
|
||||
extra_items: Option<Obj>,
|
||||
extra_title: Option<TString<'static>>,
|
||||
verb_cancel: Option<TString<'static>>,
|
||||
@ -371,13 +372,17 @@ impl FirmwareUI for UIDelizia {
|
||||
let mut summary_params = ShowInfoParams::new(title.unwrap_or(TString::empty()))
|
||||
.with_menu_button()
|
||||
.with_swipeup_footer(None);
|
||||
summary_params = unwrap!(summary_params.add(amount_label, amount));
|
||||
if let Some(amount) = amount {
|
||||
if let Some(amount_label) = amount_label {
|
||||
summary_params = unwrap!(summary_params.add(amount_label, amount));
|
||||
}
|
||||
}
|
||||
summary_params = unwrap!(summary_params.add(fee_label, fee));
|
||||
|
||||
// collect available info
|
||||
let account_params = if let Some(items) = account_items {
|
||||
let mut account_params =
|
||||
ShowInfoParams::new(TR::send__send_from.into()).with_cancel_button();
|
||||
let account_title = account_title.unwrap_or(TR::send__send_from.into());
|
||||
let mut account_params = ShowInfoParams::new(account_title).with_cancel_button();
|
||||
for pair in IterBuf::new().try_iterate(items)? {
|
||||
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||
account_params = unwrap!(account_params.add(label, value));
|
||||
|
@ -130,13 +130,14 @@ pub trait FirmwareUI {
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn confirm_summary(
|
||||
amount: TString<'static>,
|
||||
amount_label: TString<'static>,
|
||||
amount: Option<TString<'static>>,
|
||||
amount_label: Option<TString<'static>>,
|
||||
fee: TString<'static>,
|
||||
fee_label: TString<'static>,
|
||||
title: Option<TString<'static>>,
|
||||
account_items: Option<Obj>, // TODO: replace Obj
|
||||
extra_items: Option<Obj>, // TODO: replace Obj
|
||||
account_title: Option<TString<'static>>,
|
||||
extra_items: Option<Obj>, // TODO: replace Obj
|
||||
extra_title: Option<TString<'static>>,
|
||||
verb_cancel: Option<TString<'static>>,
|
||||
) -> Result<impl LayoutMaybeTrace, Error>;
|
||||
|
@ -451,11 +451,11 @@ Q(apps.ethereum.helpers)
|
||||
Q(apps.ethereum.keychain)
|
||||
Q(apps.ethereum.layout)
|
||||
Q(apps.ethereum.networks)
|
||||
Q(apps.ethereum.sc_constants)
|
||||
Q(apps.ethereum.sign_message)
|
||||
Q(apps.ethereum.sign_tx)
|
||||
Q(apps.ethereum.sign_tx_eip1559)
|
||||
Q(apps.ethereum.sign_typed_data)
|
||||
Q(apps.ethereum.staking_tx_constants)
|
||||
Q(apps.ethereum.tokens)
|
||||
Q(apps.ethereum.verify_message)
|
||||
Q(apps.monero)
|
||||
@ -647,6 +647,7 @@ Q(readwriter)
|
||||
Q(remove_resident_credential)
|
||||
Q(resident_credentials)
|
||||
Q(ripple)
|
||||
Q(sc_constants)
|
||||
Q(seed)
|
||||
Q(serialize)
|
||||
Q(serialize_messages)
|
||||
@ -657,7 +658,6 @@ Q(sign_typed_data)
|
||||
Q(signer)
|
||||
Q(signing)
|
||||
Q(solana)
|
||||
Q(staking_tx_constants)
|
||||
Q(state)
|
||||
Q(stellar)
|
||||
Q(step_01_init_transaction)
|
||||
|
@ -271,12 +271,13 @@ def confirm_reset_device(recovery: bool) -> LayoutObj[UiResult]:
|
||||
# rust/src/ui/api/firmware_micropython.rs
|
||||
def confirm_summary(
|
||||
*,
|
||||
amount: str,
|
||||
amount_label: str,
|
||||
amount: str | None,
|
||||
amount_label: str | None,
|
||||
fee: str,
|
||||
fee_label: str,
|
||||
title: str | None = None,
|
||||
account_items: Iterable[tuple[str, str]] | None = None,
|
||||
account_title: str | None = None,
|
||||
extra_items: Iterable[tuple[str, str]] | None = None,
|
||||
extra_title: str | None = None,
|
||||
verb_cancel: str | None = None,
|
||||
|
@ -286,6 +286,17 @@ class TR:
|
||||
eos__vote_for_proxy: str = "Vote for proxy"
|
||||
eos__voter: str = "Voter:"
|
||||
ethereum__amount_sent: str = "Amount sent:"
|
||||
ethereum__approve: str = "Approve"
|
||||
ethereum__approve_amount_allowance: str = "Amount allowance"
|
||||
ethereum__approve_chain_id: str = "Chain ID"
|
||||
ethereum__approve_intro: str = "Review details to approve token spending."
|
||||
ethereum__approve_intro_revoke: str = "Review details to revoke token approval."
|
||||
ethereum__approve_intro_title: str = "Token approval"
|
||||
ethereum__approve_intro_title_revoke: str = "Token revocation"
|
||||
ethereum__approve_revoke: str = "Revoke"
|
||||
ethereum__approve_revoke_from: str = "Revoke from"
|
||||
ethereum__approve_to: str = "Approve to"
|
||||
ethereum__approve_unlimited_template: str = "Approving unlimited amount of {0}"
|
||||
ethereum__contract: str = "Contract:"
|
||||
ethereum__data_size_template: str = "Size: {0} bytes"
|
||||
ethereum__gas_limit: str = "Gas limit"
|
||||
@ -785,7 +796,6 @@ class TR:
|
||||
solana__stake_question: str = "Stake SOL?"
|
||||
solana__stake_withdrawal_warning: str = "The current wallet isn't the SOL staking withdraw authority."
|
||||
solana__stake_withdrawal_warning_title: str = "Withdraw authority address"
|
||||
solana__title_token: str = "Token"
|
||||
solana__transaction_contains_unknown_instructions: str = "Transaction contains unknown instructions."
|
||||
solana__transaction_fee: str = "Transaction fee"
|
||||
solana__transaction_requires_x_signers_template: str = "Transaction requires {0} signers which increases the fee."
|
||||
@ -937,6 +947,7 @@ class TR:
|
||||
words__blockhash: str = "Blockhash"
|
||||
words__buying: str = "Buying"
|
||||
words__cancel_and_exit: str = "Cancel and exit"
|
||||
words__chain: str = "Chain"
|
||||
words__confirm: str = "Confirm"
|
||||
words__confirm_fee: str = "Confirm fee"
|
||||
words__contains: str = "Contains"
|
||||
@ -973,8 +984,10 @@ class TR:
|
||||
words__title_success: str = "Success"
|
||||
words__title_summary: str = "Summary"
|
||||
words__title_threshold: str = "Threshold"
|
||||
words__token: str = "Token"
|
||||
words__try_again: str = "Try again."
|
||||
words__unknown: str = "Unknown"
|
||||
words__unlimited: str = "Unlimited"
|
||||
words__unlocked: str = "Unlocked"
|
||||
words__warning: str = "Warning"
|
||||
words__writable: str = "Writable"
|
||||
|
@ -27,6 +27,54 @@ if TYPE_CHECKING:
|
||||
)
|
||||
|
||||
|
||||
async def require_confirm_approve(
|
||||
to_bytes: bytes,
|
||||
value: int | None,
|
||||
address_n: list[int],
|
||||
maximum_fee: str,
|
||||
fee_info_items: Iterable[tuple[str, str]],
|
||||
chain_id: int,
|
||||
network: EthereumNetworkInfo,
|
||||
token: EthereumTokenInfo,
|
||||
token_address: bytes,
|
||||
chunkify: bool,
|
||||
) -> None:
|
||||
from trezor.ui.layouts import confirm_ethereum_approve
|
||||
|
||||
from apps.ethereum.sc_constants import APPROVE_KNOWN_ADDRESSES as KNOWN_ADDRESSES
|
||||
|
||||
from . import networks, tokens
|
||||
|
||||
if to_bytes in KNOWN_ADDRESSES:
|
||||
to_str = KNOWN_ADDRESSES[to_bytes]
|
||||
chunkify = False
|
||||
else:
|
||||
to_str = address_from_bytes(to_bytes, network)
|
||||
chain_id_str = f"{chain_id} ({hex(chain_id)})"
|
||||
token_address_str = address_from_bytes(token_address, network)
|
||||
total_amount = (
|
||||
format_ethereum_amount(value, token, network) if value is not None else None
|
||||
)
|
||||
account, account_path = get_account_and_path(address_n)
|
||||
|
||||
await confirm_ethereum_approve(
|
||||
to_str,
|
||||
token is tokens.UNKNOWN_TOKEN,
|
||||
token_address_str,
|
||||
token.symbol,
|
||||
network is networks.UNKNOWN_NETWORK,
|
||||
chain_id_str,
|
||||
network.name,
|
||||
value == 0,
|
||||
total_amount,
|
||||
account,
|
||||
account_path,
|
||||
maximum_fee,
|
||||
fee_info_items,
|
||||
chunkify=chunkify,
|
||||
)
|
||||
|
||||
|
||||
async def require_confirm_tx(
|
||||
to_bytes: bytes,
|
||||
value: int,
|
||||
@ -141,36 +189,30 @@ async def require_confirm_claim(
|
||||
)
|
||||
|
||||
|
||||
async def require_confirm_unknown_token(address_bytes: bytes) -> None:
|
||||
from ubinascii import hexlify
|
||||
|
||||
from trezor.ui.layouts import (
|
||||
confirm_address,
|
||||
confirm_ethereum_unknown_contract_warning,
|
||||
)
|
||||
async def require_confirm_unknown_token() -> None:
|
||||
from trezor.ui.layouts import confirm_ethereum_unknown_contract_warning
|
||||
|
||||
await confirm_ethereum_unknown_contract_warning()
|
||||
|
||||
contract_address_hex = "0x" + hexlify(address_bytes).decode()
|
||||
await confirm_address(
|
||||
TR.words__address,
|
||||
contract_address_hex,
|
||||
subtitle=TR.ethereum__token_contract,
|
||||
verb=TR.buttons__continue,
|
||||
br_name="unknown_token",
|
||||
br_code=ButtonRequestType.SignTx,
|
||||
)
|
||||
|
||||
|
||||
def require_confirm_address(address_bytes: bytes) -> Awaitable[None]:
|
||||
def require_confirm_address(
|
||||
address_bytes: bytes,
|
||||
title: str | None = None,
|
||||
subtitle: str | None = None,
|
||||
verb: str | None = None,
|
||||
br_name: str | None = None,
|
||||
) -> Awaitable[None]:
|
||||
from ubinascii import hexlify
|
||||
|
||||
from trezor.ui.layouts import confirm_address
|
||||
|
||||
address_hex = "0x" + hexlify(address_bytes).decode()
|
||||
return confirm_address(
|
||||
TR.ethereum__title_signing_address,
|
||||
title or TR.ethereum__title_signing_address,
|
||||
address_hex,
|
||||
subtitle=subtitle,
|
||||
verb=verb,
|
||||
br_name=br_name,
|
||||
br_code=ButtonRequestType.SignTx,
|
||||
)
|
||||
|
||||
|
@ -4,12 +4,20 @@ from ubinascii import unhexlify
|
||||
# smart contract 'data' field lengths in bytes
|
||||
SC_FUNC_SIG_BYTES = const(4)
|
||||
SC_ARGUMENT_BYTES = const(32)
|
||||
SC_ARGUMENT_ADDRESS_BYTES = const(20)
|
||||
|
||||
# staking operations function signatures
|
||||
assert SC_ARGUMENT_ADDRESS_BYTES <= SC_ARGUMENT_BYTES
|
||||
|
||||
# Known ERC-20 functions
|
||||
|
||||
SC_FUNC_SIG_TRANSFER = unhexlify("a9059cbb")
|
||||
SC_FUNC_SIG_APPROVE = unhexlify("095ea7b3")
|
||||
SC_FUNC_SIG_STAKE = unhexlify("3a29dbae")
|
||||
SC_FUNC_SIG_UNSTAKE = unhexlify("76ec871c")
|
||||
SC_FUNC_SIG_CLAIM = unhexlify("33986ffa")
|
||||
|
||||
# Everstake staking
|
||||
|
||||
# addresses for pool (stake/unstake) and accounting (claim) operations
|
||||
ADDRESSES_POOL = (
|
||||
unhexlify("AFA848357154a6a624686b348303EF9a13F63264"), # holesky testnet
|
||||
@ -19,3 +27,11 @@ ADDRESSES_ACCOUNTING = (
|
||||
unhexlify("624087DD1904ab122A32878Ce9e933C7071F53B9"), # holesky testnet
|
||||
unhexlify("7a7f0b3c23C23a31cFcb0c44709be70d4D545c6e"), # mainnet
|
||||
)
|
||||
|
||||
# Approve known addresses
|
||||
# This should eventually grow into a more comprehensive database and stored in some other way,
|
||||
# but for now let's just keep a few known addresses here!
|
||||
|
||||
APPROVE_KNOWN_ADDRESSES = {
|
||||
unhexlify("e592427a0aece92de3edee1f18e0157c05861564"): "Uniswap V3 Router",
|
||||
}
|
@ -5,7 +5,7 @@ from trezor.messages import EthereumTxRequest
|
||||
from trezor.utils import BufferReader
|
||||
from trezor.wire import DataError
|
||||
|
||||
from apps.ethereum import staking_tx_constants as constants
|
||||
from apps.ethereum import sc_constants as constants
|
||||
|
||||
from .helpers import bytes_from_address
|
||||
from .keychain import with_keychain_from_chain_id
|
||||
@ -123,31 +123,56 @@ async def confirm_tx_data(
|
||||
data_total_len: int,
|
||||
) -> None:
|
||||
# function distinguishes between staking / smart contracts / regular transactions
|
||||
from .layout import require_confirm_other_data, require_confirm_tx
|
||||
from .layout import (
|
||||
require_confirm_approve,
|
||||
require_confirm_other_data,
|
||||
require_confirm_tx,
|
||||
)
|
||||
|
||||
if await handle_staking(msg, defs.network, address_bytes, maximum_fee, fee_items):
|
||||
return
|
||||
|
||||
# Handle ERC-20, currently only 'transfer' function
|
||||
token, recipient, value = await _handle_erc20_transfer(msg, defs, address_bytes)
|
||||
|
||||
is_contract_interaction = token is None and data_total_len > 0
|
||||
|
||||
if is_contract_interaction:
|
||||
await require_confirm_other_data(msg.data_initial_chunk, data_total_len)
|
||||
|
||||
await require_confirm_tx(
|
||||
recipient,
|
||||
value,
|
||||
msg.address_n,
|
||||
maximum_fee,
|
||||
fee_items,
|
||||
defs.network,
|
||||
token,
|
||||
is_contract_interaction=is_contract_interaction,
|
||||
chunkify=bool(msg.chunkify),
|
||||
# Handle ERC-20 known functions
|
||||
token, token_address, func_sig, recipient, value = await _handle_erc20(
|
||||
msg, defs, address_bytes
|
||||
)
|
||||
|
||||
if func_sig == constants.SC_FUNC_SIG_APPROVE:
|
||||
assert token
|
||||
assert token_address
|
||||
await require_confirm_approve(
|
||||
recipient,
|
||||
value,
|
||||
msg.address_n,
|
||||
maximum_fee,
|
||||
fee_items,
|
||||
msg.chain_id,
|
||||
defs.network,
|
||||
token,
|
||||
token_address,
|
||||
chunkify=bool(msg.chunkify),
|
||||
)
|
||||
else:
|
||||
assert func_sig == constants.SC_FUNC_SIG_TRANSFER or func_sig is None
|
||||
|
||||
is_contract_interaction = token is None and data_total_len > 0
|
||||
|
||||
if is_contract_interaction:
|
||||
await require_confirm_other_data(msg.data_initial_chunk, data_total_len)
|
||||
|
||||
assert value is not None
|
||||
await require_confirm_tx(
|
||||
recipient,
|
||||
value,
|
||||
msg.address_n,
|
||||
maximum_fee,
|
||||
fee_items,
|
||||
defs.network,
|
||||
token,
|
||||
is_contract_interaction=is_contract_interaction,
|
||||
chunkify=bool(msg.chunkify),
|
||||
)
|
||||
|
||||
|
||||
async def handle_staking(
|
||||
msg: MsgInSignTx,
|
||||
@ -191,16 +216,27 @@ async def handle_staking(
|
||||
return False
|
||||
|
||||
|
||||
async def _handle_erc20_transfer(
|
||||
async def _handle_erc20(
|
||||
msg: MsgInSignTx,
|
||||
definitions: Definitions,
|
||||
address_bytes: bytes,
|
||||
) -> tuple[EthereumTokenInfo | None, bytes, int]:
|
||||
from . import tokens
|
||||
from .layout import require_confirm_unknown_token
|
||||
) -> tuple[EthereumTokenInfo | None, bytes | None, bytes | None, bytes, int | None]:
|
||||
from trezor import TR
|
||||
|
||||
from . import tokens
|
||||
from .layout import require_confirm_address, require_confirm_unknown_token
|
||||
|
||||
# local_cache_attribute
|
||||
data_initial_chunk = msg.data_initial_chunk
|
||||
SC_FUNC_SIG_BYTES = constants.SC_FUNC_SIG_BYTES
|
||||
SC_ARGUMENT_BYTES = constants.SC_ARGUMENT_BYTES
|
||||
SC_ARGUMENT_ADDRESS_BYTES = constants.SC_ARGUMENT_ADDRESS_BYTES
|
||||
SC_FUNC_SIG_APPROVE = constants.SC_FUNC_SIG_APPROVE
|
||||
SC_FUNC_SIG_TRANSFER = constants.SC_FUNC_SIG_TRANSFER
|
||||
|
||||
data_initial_chunk = msg.data_initial_chunk # local_cache_attribute
|
||||
token = None
|
||||
token_address = None
|
||||
func_sig = None
|
||||
recipient = address_bytes
|
||||
value = int.from_bytes(msg.value, "big")
|
||||
if (
|
||||
@ -208,17 +244,51 @@ async def _handle_erc20_transfer(
|
||||
and len(msg.value) == 0
|
||||
and msg.data_length == 68
|
||||
and len(data_initial_chunk) == 68
|
||||
and data_initial_chunk[:16]
|
||||
== b"\xa9\x05\x9c\xbb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
):
|
||||
data_reader = BufferReader(data_initial_chunk)
|
||||
if data_reader.remaining_count() < SC_FUNC_SIG_BYTES:
|
||||
return token, token_address, func_sig, recipient, value
|
||||
|
||||
func_sig = data_reader.read_memoryview(SC_FUNC_SIG_BYTES)
|
||||
if func_sig in (SC_FUNC_SIG_TRANSFER, SC_FUNC_SIG_APPROVE):
|
||||
# The two functions happen to have the exact same parameters, so we treat them together.
|
||||
# This will need to be made into a more generic solution eventually.
|
||||
|
||||
# arg0: address, Address, 20 bytes (left padded with zeroes)
|
||||
# arg1: value, uint256, 32 bytes
|
||||
if data_reader.remaining_count() < SC_FUNC_SIG_BYTES * 2:
|
||||
return token, token_address, None, recipient, value
|
||||
arg0 = data_reader.read_memoryview(SC_ARGUMENT_BYTES)
|
||||
assert all(
|
||||
byte == 0
|
||||
for byte in arg0[: SC_ARGUMENT_BYTES - SC_ARGUMENT_ADDRESS_BYTES]
|
||||
)
|
||||
recipient = bytes(arg0[SC_ARGUMENT_BYTES - SC_ARGUMENT_ADDRESS_BYTES :])
|
||||
arg1 = data_reader.read_memoryview(SC_ARGUMENT_BYTES)
|
||||
if func_sig == SC_FUNC_SIG_APPROVE and all(byte == 255 for byte in arg1):
|
||||
# "Unlimited" approval (all bits set) is a special case
|
||||
# which we encode as value=None internally.
|
||||
value = None
|
||||
else:
|
||||
value = int.from_bytes(arg1, "big")
|
||||
|
||||
token = definitions.get_token(address_bytes)
|
||||
recipient = data_initial_chunk[16:36]
|
||||
value = int.from_bytes(data_initial_chunk[36:68], "big")
|
||||
token_address = address_bytes
|
||||
|
||||
if token is tokens.UNKNOWN_TOKEN:
|
||||
await require_confirm_unknown_token(address_bytes)
|
||||
await require_confirm_unknown_token()
|
||||
if func_sig != SC_FUNC_SIG_APPROVE:
|
||||
# For unknown tokens we also show the token address immediately after the warning
|
||||
# except in the case of the "approve" flow which shows the token address later on!
|
||||
await require_confirm_address(
|
||||
address_bytes,
|
||||
TR.words__address,
|
||||
TR.ethereum__token_contract,
|
||||
TR.buttons__continue,
|
||||
"unknown_token",
|
||||
)
|
||||
|
||||
return token, recipient, value
|
||||
return token, token_address, func_sig, recipient, value
|
||||
|
||||
|
||||
def _get_total_length(msg: EthereumSignTx, data_total: int) -> int:
|
||||
|
@ -325,7 +325,7 @@ async def confirm_token_transfer(
|
||||
value = token.name + "\n" + base58.encode(token.mint)
|
||||
|
||||
await confirm_value(
|
||||
title=TR.solana__title_token,
|
||||
title=TR.words__token,
|
||||
value=value,
|
||||
description="",
|
||||
br_name="confirm_token_address",
|
||||
|
@ -637,14 +637,14 @@ def confirm_address(
|
||||
description: str | None = None,
|
||||
verb: str | None = None,
|
||||
chunkify: bool = True,
|
||||
br_name: str = "confirm_address",
|
||||
br_name: str | None = None,
|
||||
br_code: ButtonRequestType = BR_CODE_OTHER,
|
||||
) -> Awaitable[None]:
|
||||
return confirm_value(
|
||||
title,
|
||||
address,
|
||||
description or subtitle or "",
|
||||
br_name,
|
||||
br_name or "confirm_address",
|
||||
br_code,
|
||||
subtitle=None,
|
||||
verb=(verb or TR.buttons__confirm),
|
||||
@ -700,6 +700,7 @@ def confirm_value(
|
||||
is_data: bool = True,
|
||||
info_items: Iterable[tuple[str, str]] | None = None,
|
||||
info_title: str | None = None,
|
||||
chunkify: bool = False,
|
||||
chunkify_info: bool = False,
|
||||
) -> Awaitable[None]:
|
||||
"""General confirmation dialog, used by many other confirm_* functions."""
|
||||
@ -720,6 +721,7 @@ def confirm_value(
|
||||
value=value,
|
||||
description=description,
|
||||
is_data=is_data,
|
||||
chunkify=chunkify,
|
||||
subtitle=subtitle,
|
||||
verb=verb,
|
||||
verb_cancel=verb_cancel,
|
||||
@ -793,8 +795,8 @@ def confirm_total(
|
||||
|
||||
|
||||
def _confirm_summary(
|
||||
amount: str,
|
||||
amount_label: str,
|
||||
amount: str | None,
|
||||
amount_label: str | None,
|
||||
fee: str,
|
||||
fee_label: str,
|
||||
title: str | None = None,
|
||||
@ -898,6 +900,109 @@ if not utils.BITCOIN_ONLY:
|
||||
else:
|
||||
break
|
||||
|
||||
async def confirm_ethereum_approve(
|
||||
recipient: str,
|
||||
is_unknown_token: bool,
|
||||
token_address: str,
|
||||
token_symbol: str,
|
||||
is_unknown_network: bool,
|
||||
chain_id: str,
|
||||
network_name: str,
|
||||
is_revoke: bool,
|
||||
total_amount: str | None,
|
||||
account: str | None,
|
||||
account_path: str | None,
|
||||
maximum_fee: str,
|
||||
fee_info_items: Iterable[tuple[str, str]],
|
||||
chunkify: bool = False,
|
||||
) -> None:
|
||||
await confirm_value(
|
||||
(
|
||||
TR.ethereum__approve_intro_title_revoke
|
||||
if is_revoke
|
||||
else TR.ethereum__approve_intro_title
|
||||
),
|
||||
(
|
||||
TR.ethereum__approve_intro_revoke
|
||||
if is_revoke
|
||||
else TR.ethereum__approve_intro
|
||||
),
|
||||
None,
|
||||
verb=TR.buttons__continue,
|
||||
is_data=False,
|
||||
br_name="confirm_ethereum_approve",
|
||||
)
|
||||
|
||||
await confirm_value(
|
||||
TR.ethereum__approve_revoke_from if is_revoke else TR.ethereum__approve_to,
|
||||
recipient,
|
||||
None,
|
||||
verb=TR.buttons__continue,
|
||||
br_name="confirm_ethereum_approve",
|
||||
chunkify=chunkify,
|
||||
)
|
||||
|
||||
if total_amount is None:
|
||||
await show_warning(
|
||||
"confirm_ethereum_approve",
|
||||
TR.ethereum__approve_unlimited_template.format(token_symbol),
|
||||
)
|
||||
|
||||
if is_unknown_token:
|
||||
await confirm_value(
|
||||
TR.words__address,
|
||||
token_address,
|
||||
None,
|
||||
verb=TR.buttons__continue,
|
||||
subtitle=TR.ethereum__token_contract,
|
||||
br_name="confirm_ethereum_approve",
|
||||
chunkify=chunkify,
|
||||
)
|
||||
|
||||
if is_unknown_network:
|
||||
assert is_unknown_token
|
||||
await confirm_value(
|
||||
TR.ethereum__approve_chain_id,
|
||||
chain_id,
|
||||
None,
|
||||
verb=TR.buttons__continue,
|
||||
br_name="confirm_ethereum_approve",
|
||||
)
|
||||
|
||||
properties = (
|
||||
[(TR.words__token, token_symbol)]
|
||||
if is_revoke
|
||||
else [
|
||||
(
|
||||
f"{TR.ethereum__approve_amount_allowance}:",
|
||||
total_amount or TR.words__unlimited,
|
||||
)
|
||||
]
|
||||
)
|
||||
if not is_unknown_network:
|
||||
properties.append((f"{TR.words__chain}:", network_name))
|
||||
await confirm_properties(
|
||||
"confirm_ethereum_approve",
|
||||
TR.ethereum__approve_revoke if is_revoke else TR.ethereum__approve,
|
||||
properties,
|
||||
False,
|
||||
)
|
||||
|
||||
account_items = []
|
||||
if account_path:
|
||||
account_items.append((TR.address_details__derivation_path, account_path))
|
||||
|
||||
await _confirm_summary(
|
||||
None,
|
||||
None,
|
||||
maximum_fee,
|
||||
TR.send__maximum_fee,
|
||||
TR.words__title_summary,
|
||||
account_items,
|
||||
fee_info_items,
|
||||
TR.confirm_total__title_fee,
|
||||
)
|
||||
|
||||
async def confirm_ethereum_staking_tx(
|
||||
title: str,
|
||||
intro_question: str,
|
||||
|
@ -672,11 +672,11 @@ def confirm_address(
|
||||
description: str | None = None,
|
||||
verb: str | None = None,
|
||||
chunkify: bool = True,
|
||||
br_name: str = "confirm_address",
|
||||
br_name: str | None = None,
|
||||
br_code: ButtonRequestType = BR_CODE_OTHER,
|
||||
) -> Awaitable[None]:
|
||||
return confirm_blob(
|
||||
br_name,
|
||||
br_name or "confirm_address",
|
||||
subtitle or title,
|
||||
address,
|
||||
description,
|
||||
@ -763,6 +763,7 @@ async def confirm_value(
|
||||
hold: bool = False,
|
||||
is_data: bool = True,
|
||||
info_items: Iterable[tuple[str, str]] | None = None,
|
||||
chunkify: bool = False,
|
||||
chunkify_info: bool = False,
|
||||
) -> None:
|
||||
"""General confirmation dialog, used by many other confirm_* functions."""
|
||||
@ -781,6 +782,7 @@ async def confirm_value(
|
||||
info=False,
|
||||
hold=hold,
|
||||
is_data=is_data,
|
||||
chunkify=chunkify,
|
||||
),
|
||||
br_name,
|
||||
br_code,
|
||||
@ -873,6 +875,117 @@ if not utils.BITCOIN_ONLY:
|
||||
"unknown_contract_warning", TR.ethereum__unknown_contract_address_short
|
||||
)
|
||||
|
||||
async def confirm_ethereum_approve(
|
||||
recipient: str,
|
||||
is_unknown_token: bool,
|
||||
token_address: str,
|
||||
token_symbol: str,
|
||||
is_unknown_network: bool,
|
||||
chain_id: str,
|
||||
network_name: str,
|
||||
is_revoke: bool,
|
||||
total_amount: str | None,
|
||||
account: str | None,
|
||||
account_path: str | None,
|
||||
maximum_fee: str,
|
||||
fee_info_items: Iterable[tuple[str, str]],
|
||||
chunkify: bool = False,
|
||||
) -> None:
|
||||
await confirm_value(
|
||||
(
|
||||
TR.ethereum__approve_intro_title_revoke
|
||||
if is_revoke
|
||||
else TR.ethereum__approve_intro_title
|
||||
),
|
||||
(
|
||||
TR.ethereum__approve_intro_revoke
|
||||
if is_revoke
|
||||
else TR.ethereum__approve_intro
|
||||
),
|
||||
None,
|
||||
verb=TR.buttons__continue,
|
||||
hold=False,
|
||||
is_data=False,
|
||||
br_name="confirm_ethereum_approve",
|
||||
)
|
||||
|
||||
await confirm_value(
|
||||
TR.ethereum__approve_revoke_from if is_revoke else TR.ethereum__approve_to,
|
||||
recipient,
|
||||
None,
|
||||
verb=TR.buttons__continue,
|
||||
hold=False,
|
||||
br_name="confirm_ethereum_approve",
|
||||
chunkify=chunkify,
|
||||
)
|
||||
|
||||
if total_amount is None:
|
||||
await show_warning(
|
||||
"confirm_ethereum_approve",
|
||||
TR.ethereum__approve_unlimited_template.format(token_symbol),
|
||||
TR.words__continue_anyway_question,
|
||||
)
|
||||
|
||||
if is_unknown_token:
|
||||
await confirm_value(
|
||||
TR.ethereum__token_contract + " | " + TR.words__address,
|
||||
token_address,
|
||||
None,
|
||||
verb="V",
|
||||
hold=False,
|
||||
br_name="confirm_ethereum_approve",
|
||||
chunkify=chunkify,
|
||||
)
|
||||
|
||||
if is_unknown_network:
|
||||
assert is_unknown_token
|
||||
await confirm_value(
|
||||
TR.ethereum__approve_chain_id,
|
||||
chain_id,
|
||||
None,
|
||||
verb="V",
|
||||
hold=False,
|
||||
br_name="confirm_ethereum_approve",
|
||||
)
|
||||
|
||||
properties = (
|
||||
[(TR.words__token, token_symbol)]
|
||||
if is_revoke
|
||||
else [
|
||||
(
|
||||
TR.ethereum__approve_amount_allowance,
|
||||
total_amount or TR.words__unlimited,
|
||||
)
|
||||
]
|
||||
)
|
||||
if not is_unknown_network:
|
||||
properties.append((TR.words__chain, network_name))
|
||||
await confirm_properties(
|
||||
"confirm_ethereum_approve",
|
||||
TR.ethereum__approve_revoke if is_revoke else TR.ethereum__approve,
|
||||
properties,
|
||||
False,
|
||||
)
|
||||
|
||||
account_items = []
|
||||
if account_path:
|
||||
account_items.append((TR.address_details__derivation_path, account_path))
|
||||
|
||||
await raise_if_not_confirmed(
|
||||
trezorui_api.confirm_summary(
|
||||
amount=None,
|
||||
amount_label=None,
|
||||
fee=maximum_fee,
|
||||
fee_label=f"{TR.send__maximum_fee}:",
|
||||
title=TR.words__title_summary,
|
||||
account_items=[(f"{k}:", v) for (k, v) in account_items],
|
||||
account_title=TR.address_details__account_info,
|
||||
extra_items=fee_info_items,
|
||||
extra_title=TR.confirm_total__title_fee,
|
||||
),
|
||||
br_name="confirm_ethereum_approve",
|
||||
)
|
||||
|
||||
async def confirm_ethereum_staking_tx(
|
||||
title: str,
|
||||
intro_question: str,
|
||||
|
@ -557,14 +557,14 @@ def confirm_address(
|
||||
description: str | None = None,
|
||||
verb: str | None = None,
|
||||
chunkify: bool = True,
|
||||
br_name: str = "confirm_address",
|
||||
br_name: str | None = None,
|
||||
br_code: ButtonRequestType = BR_CODE_OTHER,
|
||||
) -> Awaitable[None]:
|
||||
return confirm_value(
|
||||
title,
|
||||
address,
|
||||
description or "",
|
||||
br_name,
|
||||
br_name or "confirm_address",
|
||||
br_code,
|
||||
subtitle=subtitle,
|
||||
verb=(verb or TR.buttons__confirm),
|
||||
@ -713,8 +713,8 @@ def confirm_total(
|
||||
|
||||
|
||||
def _confirm_summary(
|
||||
amount: str,
|
||||
amount_label: str,
|
||||
amount: str | None,
|
||||
amount_label: str | None,
|
||||
fee: str,
|
||||
fee_label: str,
|
||||
title: str | None = None,
|
||||
@ -797,6 +797,105 @@ if not utils.BITCOIN_ONLY:
|
||||
None,
|
||||
)
|
||||
|
||||
async def confirm_ethereum_approve(
|
||||
recipient: str,
|
||||
is_unknown_token: bool,
|
||||
token_address: str,
|
||||
token_symbol: str,
|
||||
is_unknown_network: bool,
|
||||
chain_id: str,
|
||||
network_name: str,
|
||||
is_revoke: bool,
|
||||
total_amount: str | None,
|
||||
account: str | None,
|
||||
account_path: str | None,
|
||||
maximum_fee: str,
|
||||
fee_info_items: Iterable[tuple[str, str]],
|
||||
chunkify: bool = False,
|
||||
) -> None:
|
||||
await confirm_value(
|
||||
(
|
||||
TR.ethereum__approve_intro_title_revoke
|
||||
if is_revoke
|
||||
else TR.ethereum__approve_intro_title
|
||||
),
|
||||
(
|
||||
TR.ethereum__approve_intro_revoke
|
||||
if is_revoke
|
||||
else TR.ethereum__approve_intro
|
||||
),
|
||||
"",
|
||||
is_data=False,
|
||||
br_name="confirm_ethereum_approve",
|
||||
)
|
||||
|
||||
await confirm_value(
|
||||
TR.ethereum__approve_revoke_from if is_revoke else TR.ethereum__approve_to,
|
||||
recipient,
|
||||
"",
|
||||
chunkify=chunkify,
|
||||
br_name="confirm_ethereum_approve",
|
||||
)
|
||||
|
||||
if total_amount is None:
|
||||
await show_warning(
|
||||
"confirm_ethereum_approve",
|
||||
TR.ethereum__approve_unlimited_template.format(token_symbol),
|
||||
)
|
||||
|
||||
if is_unknown_token:
|
||||
await confirm_value(
|
||||
TR.words__address,
|
||||
token_address,
|
||||
"",
|
||||
subtitle=TR.ethereum__token_contract,
|
||||
chunkify=chunkify,
|
||||
br_name="confirm_ethereum_approve",
|
||||
)
|
||||
|
||||
if is_unknown_network:
|
||||
assert is_unknown_token
|
||||
await confirm_value(
|
||||
TR.ethereum__approve_chain_id,
|
||||
chain_id,
|
||||
"",
|
||||
br_name="confirm_ethereum_approve",
|
||||
)
|
||||
|
||||
properties = (
|
||||
[(TR.words__token, token_symbol)]
|
||||
if is_revoke
|
||||
else [
|
||||
(
|
||||
TR.ethereum__approve_amount_allowance,
|
||||
total_amount or TR.words__unlimited,
|
||||
)
|
||||
]
|
||||
)
|
||||
if not is_unknown_network:
|
||||
properties.append((TR.words__chain, network_name))
|
||||
await confirm_properties(
|
||||
"confirm_ethereum_approve",
|
||||
TR.ethereum__approve_revoke if is_revoke else TR.ethereum__approve,
|
||||
properties,
|
||||
False,
|
||||
)
|
||||
|
||||
account_items = []
|
||||
if account_path:
|
||||
account_items.append((TR.address_details__derivation_path, account_path))
|
||||
|
||||
await _confirm_summary(
|
||||
None,
|
||||
None,
|
||||
maximum_fee,
|
||||
TR.send__maximum_fee,
|
||||
TR.words__title_summary,
|
||||
account_items,
|
||||
fee_info_items,
|
||||
TR.confirm_total__title_fee,
|
||||
)
|
||||
|
||||
async def confirm_ethereum_staking_tx(
|
||||
title: str,
|
||||
intro_question: str,
|
||||
|
@ -316,6 +316,17 @@
|
||||
"eos__vote_for_proxy": "Hlasovat pro proxy",
|
||||
"eos__voter": "Hlasující osoba:",
|
||||
"ethereum__amount_sent": "Odeslaná částka:",
|
||||
"ethereum__approve": "Povolit",
|
||||
"ethereum__approve_amount_allowance": "Limit částky",
|
||||
"ethereum__approve_chain_id": "Chain ID",
|
||||
"ethereum__approve_intro_title": "Schválení tokenu",
|
||||
"ethereum__approve_intro": "Zkontrolujte data a povolte útratu tokenu.",
|
||||
"ethereum__approve_intro_title_revoke": "Odvolání tokenu",
|
||||
"ethereum__approve_intro_revoke": "Zkontrolujte a zrušte povolení tokenu.",
|
||||
"ethereum__approve_revoke": "Zrušit",
|
||||
"ethereum__approve_revoke_from": "Zrušit pro",
|
||||
"ethereum__approve_to": "Povolit pro",
|
||||
"ethereum__approve_unlimited_template": "Povolujete neomezenou částku {0}",
|
||||
"ethereum__contract": "Kontrakt:",
|
||||
"ethereum__data_size_template": "Velikost: {0} bajtů",
|
||||
"ethereum__gas_limit": "Limit gasu",
|
||||
@ -812,7 +823,6 @@
|
||||
"solana__stake_question": "Stakovat SOL?",
|
||||
"solana__stake_withdrawal_warning": "Tato pěněženka nemůže být použita pro výběr z tohoto stakingu.",
|
||||
"solana__stake_withdrawal_warning_title": "Adresa autority oprávněné k výběru",
|
||||
"solana__title_token": "Token",
|
||||
"solana__transaction_contains_unknown_instructions": "Transakce obsahuje neznámý pokyn.",
|
||||
"solana__transaction_requires_x_signers_template": "Transakce vyžaduje {0} podepisujících osob, což zvyšuje poplatek.",
|
||||
"solana__unstake": "Zrušit staking",
|
||||
@ -963,6 +973,7 @@
|
||||
"words__blockhash": "Blockhash",
|
||||
"words__buying": "Nákup",
|
||||
"words__cancel_and_exit": "Zrušit a ukončit",
|
||||
"words__chain": "Řetězec",
|
||||
"words__confirm": "Potvrdit",
|
||||
"words__confirm_fee": "Potvrdit poplatek",
|
||||
"words__contains": "Obsahuje",
|
||||
@ -999,8 +1010,10 @@
|
||||
"words__title_success": "Hotovo",
|
||||
"words__title_summary": "Souhrn",
|
||||
"words__title_threshold": "Části pro obnovu",
|
||||
"words__token": "Token",
|
||||
"words__try_again": "Zkuste to znovu.",
|
||||
"words__unknown": "Neznámé",
|
||||
"words__unlimited": "Neomezeně",
|
||||
"words__unlocked": "Odemčeno",
|
||||
"words__warning": "Varování",
|
||||
"words__writable": "Zapisovatelné",
|
||||
|
@ -316,6 +316,17 @@
|
||||
"eos__vote_for_proxy": "Vote für Proxy",
|
||||
"eos__voter": "Voter:",
|
||||
"ethereum__amount_sent": "Gesendeter Betrag:",
|
||||
"ethereum__approve": "Zulassen",
|
||||
"ethereum__approve_amount_allowance": "Betragslimit",
|
||||
"ethereum__approve_chain_id": "Chain ID",
|
||||
"ethereum__approve_intro_title": "Tokenfreigabe",
|
||||
"ethereum__approve_intro": "Prüfe Details u. lasse Token-Ausgaben zu.",
|
||||
"ethereum__approve_intro_title_revoke": "Token-Widerruf",
|
||||
"ethereum__approve_intro_revoke": "Prüfe Details u. widerrufe Tokenfreigabe.",
|
||||
"ethereum__approve_revoke": "Entziehen",
|
||||
"ethereum__approve_revoke_from": "Entziehen von",
|
||||
"ethereum__approve_to": "Zulassen für",
|
||||
"ethereum__approve_unlimited_template": "Unbegrenzten Betrag von {0} zulassen",
|
||||
"ethereum__contract": "Kontrakt:",
|
||||
"ethereum__data_size_template": "Größe: {0} Bytes",
|
||||
"ethereum__gas_limit": "Gas-Grenze",
|
||||
@ -812,7 +823,6 @@
|
||||
"solana__stake_question": "SOL Staken?",
|
||||
"solana__stake_withdrawal_warning": "Diese Wallet hat keine SOL-Staking Auszahlungsberechtigung.",
|
||||
"solana__stake_withdrawal_warning_title": "Auszahlungsautoritätsadresse",
|
||||
"solana__title_token": "Token",
|
||||
"solana__transaction_contains_unknown_instructions": "Transaktion enthält unbekannte Anweisungen.",
|
||||
"solana__transaction_requires_x_signers_template": "Transaktion erfordert {0} Unterzeichner. Dadurch steigt die Gebühr.",
|
||||
"solana__unstake": "Entstaken",
|
||||
@ -963,6 +973,7 @@
|
||||
"words__blockhash": "Blockhash",
|
||||
"words__buying": "Kaufen",
|
||||
"words__cancel_and_exit": "Abbrechen und schließen",
|
||||
"words__chain": "Chain",
|
||||
"words__confirm": "Bestätigen",
|
||||
"words__confirm_fee": "Gebühr bestätigen",
|
||||
"words__contains": "Enthält",
|
||||
@ -999,8 +1010,10 @@
|
||||
"words__title_success": "Erfolg",
|
||||
"words__title_summary": "Zusammenfassung",
|
||||
"words__title_threshold": "Schwelle",
|
||||
"words__token": "Token",
|
||||
"words__try_again": "Versuche es erneut.",
|
||||
"words__unknown": "Unbekannt",
|
||||
"words__unlimited": "Unbegrenzt",
|
||||
"words__unlocked": "Entsperrt",
|
||||
"words__warning": "Warnung",
|
||||
"words__writable": "Beschreibbar",
|
||||
|
@ -288,6 +288,17 @@
|
||||
"eos__vote_for_proxy": "Vote for proxy",
|
||||
"eos__voter": "Voter:",
|
||||
"ethereum__amount_sent": "Amount sent:",
|
||||
"ethereum__approve": "Approve",
|
||||
"ethereum__approve_amount_allowance": "Amount allowance",
|
||||
"ethereum__approve_chain_id": "Chain ID",
|
||||
"ethereum__approve_intro_title": "Token approval",
|
||||
"ethereum__approve_intro": "Review details to approve token spending.",
|
||||
"ethereum__approve_intro_title_revoke": "Token revocation",
|
||||
"ethereum__approve_intro_revoke": "Review details to revoke token approval.",
|
||||
"ethereum__approve_revoke": "Revoke",
|
||||
"ethereum__approve_revoke_from": "Revoke from",
|
||||
"ethereum__approve_to": "Approve to",
|
||||
"ethereum__approve_unlimited_template": "Approving unlimited amount of {0}",
|
||||
"ethereum__contract": "Contract:",
|
||||
"ethereum__data_size_template": "Size: {0} bytes",
|
||||
"ethereum__gas_limit": "Gas limit",
|
||||
@ -787,7 +798,6 @@
|
||||
"solana__stake_question": "Stake SOL?",
|
||||
"solana__stake_withdrawal_warning": "The current wallet isn't the SOL staking withdraw authority.",
|
||||
"solana__stake_withdrawal_warning_title": "Withdraw authority address",
|
||||
"solana__title_token": "Token",
|
||||
"solana__transaction_contains_unknown_instructions": "Transaction contains unknown instructions.",
|
||||
"solana__transaction_fee": "Transaction fee",
|
||||
"solana__transaction_requires_x_signers_template": "Transaction requires {0} signers which increases the fee.",
|
||||
@ -939,6 +949,7 @@
|
||||
"words__blockhash": "Blockhash",
|
||||
"words__buying": "Buying",
|
||||
"words__cancel_and_exit": "Cancel and exit",
|
||||
"words__chain": "Chain",
|
||||
"words__confirm": "Confirm",
|
||||
"words__confirm_fee": "Confirm fee",
|
||||
"words__contains": "Contains",
|
||||
@ -975,8 +986,10 @@
|
||||
"words__title_success": "Success",
|
||||
"words__title_summary": "Summary",
|
||||
"words__title_threshold": "Threshold",
|
||||
"words__token": "Token",
|
||||
"words__try_again": "Try again.",
|
||||
"words__unknown": "Unknown",
|
||||
"words__unlimited": "Unlimited",
|
||||
"words__unlocked": "Unlocked",
|
||||
"words__warning": "Warning",
|
||||
"words__writable": "Writable",
|
||||
|
@ -316,6 +316,17 @@
|
||||
"eos__vote_for_proxy": "Votar por representación",
|
||||
"eos__voter": "Votante:",
|
||||
"ethereum__amount_sent": "Importe enviado:",
|
||||
"ethereum__approve": "Aprobar",
|
||||
"ethereum__approve_amount_allowance": "Cantidad autorizada",
|
||||
"ethereum__approve_chain_id": "ID de cadena",
|
||||
"ethereum__approve_intro_title": "Aprobación de tokens",
|
||||
"ethereum__approve_intro": "Revisa los detalles y permite gasto de token.",
|
||||
"ethereum__approve_intro_title_revoke": "Revocación de tokens",
|
||||
"ethereum__approve_intro_revoke": "Revisa los detalles y cancela la aprobación de token.",
|
||||
"ethereum__approve_revoke": "Revocar",
|
||||
"ethereum__approve_revoke_from": "Revocar de",
|
||||
"ethereum__approve_to": "Aprobar a",
|
||||
"ethereum__approve_unlimited_template": "Aprobando una cantidad ilimitada de {0}",
|
||||
"ethereum__contract": "Contrato:",
|
||||
"ethereum__data_size_template": "Tamaño: {0} bytes",
|
||||
"ethereum__gas_limit": "Límite de gas",
|
||||
@ -817,7 +828,6 @@
|
||||
"solana__stake_question": "¿Hacer stake de SOL?",
|
||||
"solana__stake_withdrawal_warning": "El monedero actual no podrá retirar de este staking.",
|
||||
"solana__stake_withdrawal_warning_title": "Dirección de la autoridad de retiro",
|
||||
"solana__title_token": "Token",
|
||||
"solana__transaction_contains_unknown_instructions": "La transacción contiene instrucciones desconocidas.",
|
||||
"solana__transaction_requires_x_signers_template": "La transacción requiere {0} firmantes, lo que aumenta la comisión.",
|
||||
"solana__unstake": "Retirar stake",
|
||||
@ -968,6 +978,7 @@
|
||||
"words__blockhash": "Blockhash",
|
||||
"words__buying": "Comprar",
|
||||
"words__cancel_and_exit": "Cancelar y salir",
|
||||
"words__chain": "Cadena",
|
||||
"words__confirm": "Confirmar",
|
||||
"words__confirm_fee": "Confirmar comisión",
|
||||
"words__contains": "Contiene",
|
||||
@ -1004,8 +1015,10 @@
|
||||
"words__title_success": "Completado",
|
||||
"words__title_summary": "Resumen",
|
||||
"words__title_threshold": "Umbral",
|
||||
"words__token": "Token",
|
||||
"words__try_again": "Reintentar.",
|
||||
"words__unknown": "Desconocido",
|
||||
"words__unlimited": "Ilimitado",
|
||||
"words__unlocked": "Desbloqueado",
|
||||
"words__warning": "Advertencia",
|
||||
"words__writable": "Modificable",
|
||||
|
@ -316,6 +316,17 @@
|
||||
"eos__vote_for_proxy": "Voter pour le mandataire",
|
||||
"eos__voter": "Votant:",
|
||||
"ethereum__amount_sent": "Montant envoyé:",
|
||||
"ethereum__approve": "Approuver",
|
||||
"ethereum__approve_amount_allowance": "Montant autorisé",
|
||||
"ethereum__approve_chain_id": "Chaîne ID",
|
||||
"ethereum__approve_intro_title": "Autorisation de jetons",
|
||||
"ethereum__approve_intro": "Vérifiez les informations pour autoriser l'utilisation des jetons.",
|
||||
"ethereum__approve_intro_title_revoke": "Révocation de l'autorisation",
|
||||
"ethereum__approve_intro_revoke": "Vérifiez les informations pour révoquer l'autorisation des jetons.",
|
||||
"ethereum__approve_revoke": "Révoquer",
|
||||
"ethereum__approve_revoke_from": "Révoquer de",
|
||||
"ethereum__approve_to": "Approuver pour",
|
||||
"ethereum__approve_unlimited_template": "Autoriser un montant illimité de {0}",
|
||||
"ethereum__contract": "Contrat:",
|
||||
"ethereum__data_size_template": "Taille: {0} octets",
|
||||
"ethereum__gas_limit": "Limite de gaz",
|
||||
@ -812,7 +823,6 @@
|
||||
"solana__stake_question": "Staker de l'SOL ?",
|
||||
"solana__stake_withdrawal_warning": "Ce portefeuille ne pourra pas être utilisé pour retirer de ce staking.",
|
||||
"solana__stake_withdrawal_warning_title": "Adresse de l’autorité de retrait",
|
||||
"solana__title_token": "Jeton",
|
||||
"solana__transaction_contains_unknown_instructions": "La transaction contient des instructions inconnues.",
|
||||
"solana__transaction_requires_x_signers_template": "La transaction nécessite {0} signataires, ce qui augmente les frais.",
|
||||
"solana__unstake": "Unstake",
|
||||
@ -963,6 +973,7 @@
|
||||
"words__blockhash": "Hachage de bloc",
|
||||
"words__buying": "Achat",
|
||||
"words__cancel_and_exit": "Annuler et quitter",
|
||||
"words__chain": "Chaîne",
|
||||
"words__confirm": "Conf.",
|
||||
"words__confirm_fee": "Conf. les frais",
|
||||
"words__contains": "Contient",
|
||||
@ -999,8 +1010,10 @@
|
||||
"words__title_success": "Réussite",
|
||||
"words__title_summary": "Résumé",
|
||||
"words__title_threshold": "Seuil",
|
||||
"words__token": "Jeton",
|
||||
"words__try_again": "Réessayer.",
|
||||
"words__unknown": "Inconnu",
|
||||
"words__unlimited": "Illimité",
|
||||
"words__unlocked": "Déverrouillé",
|
||||
"words__warning": "Avertissement",
|
||||
"words__writable": "Modifiable",
|
||||
|
@ -316,6 +316,17 @@
|
||||
"eos__vote_for_proxy": "Voto per delega",
|
||||
"eos__voter": "Votante:",
|
||||
"ethereum__amount_sent": "Importo inviato:",
|
||||
"ethereum__approve": "Approva",
|
||||
"ethereum__approve_amount_allowance": "Limite importo",
|
||||
"ethereum__approve_chain_id": "ID chain",
|
||||
"ethereum__approve_intro_title": "Approv. token",
|
||||
"ethereum__approve_intro": "Rivedi dettagli e approva spesa token.",
|
||||
"ethereum__approve_intro_title_revoke": "Revoca token",
|
||||
"ethereum__approve_intro_revoke": "Rivedi dettagli e revoca approv. token.",
|
||||
"ethereum__approve_revoke": "Revoca",
|
||||
"ethereum__approve_revoke_from": "Revoca da",
|
||||
"ethereum__approve_to": "Approva a",
|
||||
"ethereum__approve_unlimited_template": "Approvando importo illimitato di {0}",
|
||||
"ethereum__contract": "Contratto:",
|
||||
"ethereum__data_size_template": "Dimensioni: {0} byte",
|
||||
"ethereum__gas_limit": "Limite gas:",
|
||||
@ -800,7 +811,6 @@
|
||||
"solana__is_provided_via_lookup_table_template": "{0} viene fornito tramite una tabella di ricerca.",
|
||||
"solana__lookup_table_address": "Indirizzo tabella di ricerca",
|
||||
"solana__multiple_signers": "Più firmatari",
|
||||
"solana__title_token": "Token",
|
||||
"solana__transaction_contains_unknown_instructions": "La transazione contiene istruzioni sconosciute.",
|
||||
"solana__transaction_requires_x_signers_template": "Poiché la transazione richiede {0} firmatari, la commissione è più elevata.",
|
||||
"stellar__account_merge": "Unione conti",
|
||||
@ -948,6 +958,7 @@
|
||||
"words__blockhash": "Blockhash",
|
||||
"words__buying": "Acquisto",
|
||||
"words__cancel_and_exit": "Annulla ed esci",
|
||||
"words__chain": "Catena",
|
||||
"words__confirm": "Conferma",
|
||||
"words__confirm_fee": "Conferma commissione",
|
||||
"words__contains": "Contiene",
|
||||
@ -984,8 +995,10 @@
|
||||
"words__title_success": "Operazione riuscita",
|
||||
"words__title_summary": "Riepilogo",
|
||||
"words__title_threshold": "Soglia",
|
||||
"words__token": "Token",
|
||||
"words__try_again": "Riprova.",
|
||||
"words__unknown": "Sconosciuto",
|
||||
"words__unlimited": "Illimitato",
|
||||
"words__unlocked": "Sbloccato",
|
||||
"words__warning": "Avviso",
|
||||
"words__writable": "Scrivibile",
|
||||
|
@ -998,5 +998,19 @@
|
||||
"996": "words__unlocked",
|
||||
"997": "solana__max_fees_rent",
|
||||
"998": "solana__max_rent_fee",
|
||||
"999": "solana__transaction_fee"
|
||||
"999": "solana__transaction_fee",
|
||||
"1000": "ethereum__approve",
|
||||
"1001": "ethereum__approve_amount_allowance",
|
||||
"1002": "ethereum__approve_chain_id",
|
||||
"1003": "ethereum__approve_intro",
|
||||
"1004": "ethereum__approve_intro_title",
|
||||
"1005": "ethereum__approve_to",
|
||||
"1006": "ethereum__approve_unlimited_template",
|
||||
"1007": "words__unlimited",
|
||||
"1008": "ethereum__approve_intro_revoke",
|
||||
"1009": "ethereum__approve_intro_title_revoke",
|
||||
"1010": "ethereum__approve_revoke",
|
||||
"1011": "ethereum__approve_revoke_from",
|
||||
"1012": "words__chain",
|
||||
"1013": "words__token"
|
||||
}
|
||||
|
@ -316,6 +316,17 @@
|
||||
"eos__vote_for_proxy": "Votar para proxy",
|
||||
"eos__voter": "Eleitor:",
|
||||
"ethereum__amount_sent": "Quantia enviada:",
|
||||
"ethereum__approve": "Aprovar",
|
||||
"ethereum__approve_amount_allowance": "Limite quantia",
|
||||
"ethereum__approve_chain_id": "ID chain",
|
||||
"ethereum__approve_intro_title": "Aprov. token",
|
||||
"ethereum__approve_intro": "Revise det. e aprove gasto de token.",
|
||||
"ethereum__approve_intro_title_revoke": "Revog. token",
|
||||
"ethereum__approve_intro_revoke": "Revise det. e revogue aprovação token.",
|
||||
"ethereum__approve_revoke": "Revogar",
|
||||
"ethereum__approve_revoke_from": "Revogar de",
|
||||
"ethereum__approve_to": "Aprovar a",
|
||||
"ethereum__approve_unlimited_template": "Aprovando quantia ilimitada de {0}",
|
||||
"ethereum__contract": "Contrato:",
|
||||
"ethereum__data_size_template": "Tamanho: {0} bytes",
|
||||
"ethereum__gas_limit": "Limite de gás:",
|
||||
@ -811,7 +822,6 @@
|
||||
"solana__stake_question": "Fazer stake de SOL?",
|
||||
"solana__stake_withdrawal_warning": "A carteira atual não é a autoridade de saque do staking de SOL.",
|
||||
"solana__stake_withdrawal_warning_title": "Endereço da autoridade de saque",
|
||||
"solana__title_token": "Token",
|
||||
"solana__transaction_contains_unknown_instructions": "A transação contém instruções desconhecidas.",
|
||||
"solana__transaction_requires_x_signers_template": "A transação exige {0} signatários, o que aumenta a taxa.",
|
||||
"solana__unstake": "Tirar do stake",
|
||||
@ -962,6 +972,7 @@
|
||||
"words__blockhash": "Blockhash",
|
||||
"words__buying": "Compra",
|
||||
"words__cancel_and_exit": "Cancelar e sair",
|
||||
"words__chain": "Cadeia",
|
||||
"words__confirm": "Confirmar",
|
||||
"words__confirm_fee": "Confirmar taxa",
|
||||
"words__contains": "Contém",
|
||||
@ -998,8 +1009,10 @@
|
||||
"words__title_success": "Sucesso",
|
||||
"words__title_summary": "Resumo",
|
||||
"words__title_threshold": "Limite",
|
||||
"words__token": "Token",
|
||||
"words__try_again": "Tentar novamente.",
|
||||
"words__unknown": "Desconhecido",
|
||||
"words__unlimited": "Ilimitado",
|
||||
"words__unlocked": "Desbloqueado",
|
||||
"words__warning": "Aviso",
|
||||
"words__writable": "Gravável",
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"current": {
|
||||
"merkle_root": "27c0e5fb8f144289e4e726678a0dfc3371d344ff5a7cca2233b1a4a009149653",
|
||||
"datetime": "2025-05-07T13:50:09.500899",
|
||||
"commit": "10cdd67b3e363d3e002d4736cdcb3e25ce41f186"
|
||||
"merkle_root": "b0b4e9158f15d387ed0091716fca17b9523500faff4157b54084991c8db7a868",
|
||||
"datetime": "2025-05-20T12:48:17.683102",
|
||||
"commit": "51edc03279e8bef1ad676e2f998309ee4e190fcb"
|
||||
},
|
||||
"history": [
|
||||
{
|
||||
|
@ -724,7 +724,6 @@
|
||||
"solana__is_provided_via_lookup_table_template": "{0}, bir arama tablosu aracılığıyla sağlanır.",
|
||||
"solana__lookup_table_address": "Arama tablosu adresi",
|
||||
"solana__multiple_signers": "Birden fazla imzalayan",
|
||||
"solana__title_token": "Token",
|
||||
"solana__transaction_contains_unknown_instructions": "İşlem bilinmeyen talimatlar içeriyor.",
|
||||
"solana__transaction_requires_x_signers_template": "İşlem için {0} imzalayan gerekiyor ve bu da ücreti artırıyor.",
|
||||
"stellar__account_merge": "Hesap Birleştirme",
|
||||
@ -897,6 +896,7 @@
|
||||
"words__title_success": "Başari",
|
||||
"words__title_summary": "Özet",
|
||||
"words__title_threshold": "Eşi̇k",
|
||||
"words__token": "Token",
|
||||
"words__unknown": "Bilinmeyen",
|
||||
"words__unlocked": "Kilidi Açık",
|
||||
"words__warning": "Uyarı",
|
||||
|
@ -44,16 +44,23 @@ def make_defs(parameters: dict) -> messages.EthereumDefinitions:
|
||||
# With removal of most built-in defs from firmware, we have test vectors
|
||||
# that no longer run. Because this is not the place to test the definitions,
|
||||
# we generate fake entries so that we can check the signing results.
|
||||
address_n = parse_path(parameters["path"])
|
||||
slip44 = unharden(address_n[1])
|
||||
network = encode_eth_network(chain_id=parameters["chain_id"], slip44=slip44)
|
||||
# However, we have the option to not generate the fake definitions,
|
||||
# in case what we want to test is signing a tx for an unknown chain
|
||||
# (which should be, well... not defined)!
|
||||
if parameters.get("fake_defs", True):
|
||||
address_n = parse_path(parameters["path"])
|
||||
slip44 = unharden(address_n[1])
|
||||
network = encode_eth_network(chain_id=parameters["chain_id"], slip44=slip44)
|
||||
|
||||
return messages.EthereumDefinitions(encoded_network=network)
|
||||
return messages.EthereumDefinitions(encoded_network=network)
|
||||
else:
|
||||
return messages.EthereumDefinitions()
|
||||
|
||||
|
||||
@parametrize_using_common_fixtures(
|
||||
"ethereum/sign_tx.json",
|
||||
"ethereum/sign_tx_eip155.json",
|
||||
"ethereum/sign_tx_erc20.json",
|
||||
)
|
||||
@pytest.mark.parametrize("chunkify", (True, False))
|
||||
def test_signtx(client: Client, chunkify: bool, parameters: dict, result: dict):
|
||||
|
Loading…
Reference in New Issue
Block a user