mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-26 08:08:51 +00:00
feat(core): confirm ETH stake, unstake, claim
This commit is contained in:
parent
e1f696b4dd
commit
ebcf3e2db2
128
common/tests/fixtures/ethereum/sign_tx_staking.json
vendored
Normal file
128
common/tests/fixtures/ethereum/sign_tx_staking.json
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
{
|
||||
"setup": {
|
||||
"mnemonic": "alcohol woman abuse must during monitor noble actual mixed trade anger aisle",
|
||||
"passphrase": ""
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"name": "stake_holesky",
|
||||
"parameters": {
|
||||
"comment": "Stake transaction - Holesky testnet",
|
||||
"data": "3a29dbae0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xAFA848357154a6a624686b348303EF9a13F63264",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x16345785D8A0000"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 37,
|
||||
"sig_r": "1a43606f1a3e9a61c4986cc9b324dd74f84557942cddf2e4ffccea82c2c54824",
|
||||
"sig_s": "63e8595bfa1e383a1fea7205bff5afcf3b3d3513b45a2f792396f2f4251f4c55"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "stake_main",
|
||||
"parameters": {
|
||||
"comment": "Stake transaction - Mainnet",
|
||||
"data": "3a29dbae0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xD523794C879D9eC028960a231F866758e405bE34",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x16345785D8A0000"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 37,
|
||||
"sig_r": "104ae56ff2ec396a86191fa94b6e79af20efcc28e5d16b39d90fe05c990a2ce6",
|
||||
"sig_s": "504fd80e50890df83a9b004d0ba97db11b93354327e717df5c0874036f616d47"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "unstake_holesky",
|
||||
"parameters": {
|
||||
"comment": "Unstake transaction - Holesky testnet",
|
||||
"data": "76ec871c000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xAFA848357154a6a624686b348303EF9a13F63264",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 38,
|
||||
"sig_r": "f6553486737da2ceb42067047d0e8dd0add8e82f49b524cf657215e1d2487d16",
|
||||
"sig_s": "21336e4c53537bcdf06a71ed6cbefc3374461476d8da6e988e6cf4957b6a8bb1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "unstake_main",
|
||||
"parameters": {
|
||||
"comment": "Unstake transaction - Mainnet",
|
||||
"data": "76ec871c000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xD523794C879D9eC028960a231F866758e405bE34",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 38,
|
||||
"sig_r": "09077008477f40468928a94c45bfdb0b0ff473473401cd918740af4c98734bea",
|
||||
"sig_s": "3cad467c41810a2d8901020a803d6836d1d5a39d8198eaba3eec121a48997b18"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "claim_holesky",
|
||||
"parameters": {
|
||||
"comment": "Claim transaction - Holesky testnet",
|
||||
"data": "33986ffa",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0x624087DD1904ab122A32878Ce9e933C7071F53B9",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 37,
|
||||
"sig_r": "4a77a5f7437c4744c8dc4e48f968a6fedccd86db0ee15ce6a832e71b17f11a9a",
|
||||
"sig_s": "75e8efc0ecf1484ce745bbcd19899fbc458677869f3008be5ddb9c8ad8766d40"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "claim_mainnet",
|
||||
"parameters": {
|
||||
"comment": "Claim transaction - Mainnet",
|
||||
"data": "33986ffa",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0x7a7f0b3c23C23a31cFcb0c44709be70d4D545c6e",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 37,
|
||||
"sig_r": "c477417e2471d94c089d082fe1570fa8ba114e6572211c4b539244e2d40457d1",
|
||||
"sig_s": "0bb029ed5dbe76016f74263c0a8709bcbd89affac62504196a1cf546d7c723a3"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
108
common/tests/fixtures/ethereum/sign_tx_staking_data_error.json
vendored
Normal file
108
common/tests/fixtures/ethereum/sign_tx_staking_data_error.json
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
{
|
||||
"setup": {
|
||||
"mnemonic": "alcohol woman abuse must during monitor noble actual mixed trade anger aisle",
|
||||
"passphrase": ""
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"name": "stake_bad_inputs_1",
|
||||
"parameters": {
|
||||
"comment": "Stake transaction - Holesky testnet. Wrong source argument (should be 1).",
|
||||
"data": "3a29dbae0000000000000000000000000000000000000000000000000000000000000002",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xAFA848357154a6a624686b348303EF9a13F63264",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x16345785D8A0000"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 0,
|
||||
"sig_r": "0x0",
|
||||
"sig_s": "0x0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "stake_bad_inputs_2",
|
||||
"parameters": {
|
||||
"comment": "Stake transaction - Mainnet. Missing arguments.",
|
||||
"data": "3a29dbae",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xD523794C879D9eC028960a231F866758e405bE34",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x16345785D8A0000"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 0,
|
||||
"sig_r": "0x0",
|
||||
"sig_s": "0x0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "unstake_bad_inputs_1",
|
||||
"parameters": {
|
||||
"comment": "Unstake transaction - Holesky testnet. Wrong source argument (should be 1).",
|
||||
"data": "76ec871c000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xAFA848357154a6a624686b348303EF9a13F63264",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 0,
|
||||
"sig_r": "0x0",
|
||||
"sig_s": "0x0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "unstake_bad_inputs_2",
|
||||
"parameters": {
|
||||
"comment": "Unstake transaction - Holesky testnet. Misaligned arguments.",
|
||||
"data": "76ec871c000000000000000000",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xAFA848357154a6a624686b348303EF9a13F63264",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 0,
|
||||
"sig_r": "0x0",
|
||||
"sig_s": "0x0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "claim_bad_inputs_1",
|
||||
"parameters": {
|
||||
"comment": "Claim transaction - Mainnet. Misaligned data.",
|
||||
"data": "33986ffaaa000aaa",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0x7a7f0b3c23C23a31cFcb0c44709be70d4D545c6e",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_price": "0x14",
|
||||
"gas_limit": "0x14",
|
||||
"tx_type": null,
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 0,
|
||||
"sig_r": "0x0",
|
||||
"sig_s": "0x0"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
128
common/tests/fixtures/ethereum/sign_tx_staking_eip1559.json
vendored
Normal file
128
common/tests/fixtures/ethereum/sign_tx_staking_eip1559.json
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
{
|
||||
"setup": {
|
||||
"mnemonic": "alcohol woman abuse must during monitor noble actual mixed trade anger aisle",
|
||||
"passphrase": ""
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"name": "stake_holesky",
|
||||
"parameters": {
|
||||
"comment": "Stake transaction - Holesky testnet",
|
||||
"data": "3a29dbae0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xAFA848357154a6a624686b348303EF9a13F63264",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_limit": "0x14",
|
||||
"max_gas_fee": "0x14",
|
||||
"max_priority_fee": "0x1",
|
||||
"value": "0x16345785D8A0000"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 0,
|
||||
"sig_r": "77d68a17e2bcacccb791ca3d7e298588be0511a7d3c055abbb2030f54b56c6fa",
|
||||
"sig_s": "567092e781fe1aedc458bdbc9ce4328398bf1d7d635787191881131c1afa7143"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "stake_main",
|
||||
"parameters": {
|
||||
"comment": "Stake transaction - Mainnet",
|
||||
"data": "3a29dbae0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xD523794C879D9eC028960a231F866758e405bE34",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_limit": "0x14",
|
||||
"max_gas_fee": "0x14",
|
||||
"max_priority_fee": "0x1",
|
||||
"value": "0x16345785D8A0000"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 0,
|
||||
"sig_r": "05a37bf477df7f256729ec8607aa20a6286c42a246b5deebaf6d54915c0f87e3",
|
||||
"sig_s": "30ffe2e80119452403bbf238b02def90a120ade2e73ab60926060ed10562f5ed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "unstake_holesky",
|
||||
"parameters": {
|
||||
"comment": "Unstake transaction - Holesky testnet",
|
||||
"data": "76ec871c000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xAFA848357154a6a624686b348303EF9a13F63264",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_limit": "0x14",
|
||||
"max_gas_fee": "0x14",
|
||||
"max_priority_fee": "0x1",
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 0,
|
||||
"sig_r": "9dfa73ea497785ffcb61598c554a57e46fce0a605c9ed06a4c8f265fccfd912e",
|
||||
"sig_s": "45db0bd2189d4bc4828c859d28b907b3d47c4a35eef707052cde2f4fff4ef4fe"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "unstake_main",
|
||||
"parameters": {
|
||||
"comment": "Unstake transaction - Mainnet",
|
||||
"data": "76ec871c000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0xD523794C879D9eC028960a231F866758e405bE34",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_limit": "0x14",
|
||||
"max_gas_fee": "0x14",
|
||||
"max_priority_fee": "0x1",
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 0,
|
||||
"sig_r": "15eb458e473c7f74abf99ba8833885e25435f913e77e5bc1259396942f6b9539",
|
||||
"sig_s": "14ea7a1417cfab7bb2cdbd0750aa7929f02a30999b624dd2ae1029c6be9dc9d8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "claim_holesky",
|
||||
"parameters": {
|
||||
"comment": "Claim transaction - Holesky testnet",
|
||||
"data": "33986ffa",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0x624087DD1904ab122A32878Ce9e933C7071F53B9",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_limit": "0x14",
|
||||
"max_gas_fee": "0x14",
|
||||
"max_priority_fee": "0x1",
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 1,
|
||||
"sig_r": "5abf2a99ac6431fce9e436234e2962502b1a67d4b582f90f4bc592e790f8fd7e",
|
||||
"sig_s": "60b01cd76d40f089dc9464cb442c6bbac435f80a35b5a1ed128de7cb9718aa3a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "claim_mainnet",
|
||||
"parameters": {
|
||||
"comment": "Claim transaction - Mainnet",
|
||||
"data": "33986ffa",
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"to_address": "0x7a7f0b3c23C23a31cFcb0c44709be70d4D545c6e",
|
||||
"chain_id": 1,
|
||||
"nonce": "0x0",
|
||||
"gas_limit": "0x14",
|
||||
"max_gas_fee": "0x14",
|
||||
"max_priority_fee": "0x1",
|
||||
"value": "0x0"
|
||||
},
|
||||
"result": {
|
||||
"sig_v": 0,
|
||||
"sig_r": "7b3a09f1e0b28b2a25ddc3de02168fc20e9faada1d97181679dfb6f8383382da",
|
||||
"sig_s": "6667dbb7b7b63d3e51df012a62dc6b01f13b60c7598b04aa499722cfb26864f3"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
1
core/.changelog.d/3517.added
Normal file
1
core/.changelog.d/3517.added
Normal file
@ -0,0 +1 @@
|
||||
Clear sign ETH staking transactions on Everstake pool.
|
@ -367,6 +367,14 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_ethereum__show_full_message;
|
||||
MP_QSTR_ethereum__show_full_struct;
|
||||
MP_QSTR_ethereum__sign_eip712;
|
||||
MP_QSTR_ethereum__staking_claim;
|
||||
MP_QSTR_ethereum__staking_claim_address;
|
||||
MP_QSTR_ethereum__staking_claim_intro;
|
||||
MP_QSTR_ethereum__staking_stake;
|
||||
MP_QSTR_ethereum__staking_stake_address;
|
||||
MP_QSTR_ethereum__staking_stake_intro;
|
||||
MP_QSTR_ethereum__staking_unstake;
|
||||
MP_QSTR_ethereum__staking_unstake_intro;
|
||||
MP_QSTR_ethereum__title_confirm_data;
|
||||
MP_QSTR_ethereum__title_confirm_domain;
|
||||
MP_QSTR_ethereum__title_confirm_message;
|
||||
@ -898,6 +906,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_stellar__your_account;
|
||||
MP_QSTR_subprompt;
|
||||
MP_QSTR_subtitle;
|
||||
MP_QSTR_text_mono;
|
||||
MP_QSTR_tezos__baker_address;
|
||||
MP_QSTR_tezos__balance;
|
||||
MP_QSTR_tezos__ballot;
|
||||
|
@ -844,6 +844,14 @@ pub enum TranslatedString {
|
||||
words__yes = 831,
|
||||
reboot_to_bootloader__just_a_moment = 832,
|
||||
inputs__previous = 833,
|
||||
ethereum__staking_claim = 834,
|
||||
ethereum__staking_claim_address = 835,
|
||||
ethereum__staking_claim_intro = 836,
|
||||
ethereum__staking_stake = 837,
|
||||
ethereum__staking_stake_address = 838,
|
||||
ethereum__staking_stake_intro = 839,
|
||||
ethereum__staking_unstake = 840,
|
||||
ethereum__staking_unstake_intro = 841,
|
||||
}
|
||||
|
||||
impl TranslatedString {
|
||||
@ -1683,6 +1691,14 @@ impl TranslatedString {
|
||||
Self::words__yes => "Yes",
|
||||
Self::reboot_to_bootloader__just_a_moment => "Just a moment...",
|
||||
Self::inputs__previous => "PREVIOUS",
|
||||
Self::ethereum__staking_claim => "CLAIM",
|
||||
Self::ethereum__staking_claim_address => "CLAIM ADDRESS",
|
||||
Self::ethereum__staking_claim_intro => "Claim ETH from Everstake?",
|
||||
Self::ethereum__staking_stake => "STAKE",
|
||||
Self::ethereum__staking_stake_address => "STAKE ADDRESS",
|
||||
Self::ethereum__staking_stake_intro => "Stake ETH on Everstake?",
|
||||
Self::ethereum__staking_unstake => "UNSTAKE",
|
||||
Self::ethereum__staking_unstake_intro => "Unstake ETH from Everstake?",
|
||||
}
|
||||
}
|
||||
|
||||
@ -2523,6 +2539,14 @@ impl TranslatedString {
|
||||
Qstr::MP_QSTR_words__yes => Some(Self::words__yes),
|
||||
Qstr::MP_QSTR_reboot_to_bootloader__just_a_moment => Some(Self::reboot_to_bootloader__just_a_moment),
|
||||
Qstr::MP_QSTR_inputs__previous => Some(Self::inputs__previous),
|
||||
Qstr::MP_QSTR_ethereum__staking_claim => Some(Self::ethereum__staking_claim),
|
||||
Qstr::MP_QSTR_ethereum__staking_claim_address => Some(Self::ethereum__staking_claim_address),
|
||||
Qstr::MP_QSTR_ethereum__staking_claim_intro => Some(Self::ethereum__staking_claim_intro),
|
||||
Qstr::MP_QSTR_ethereum__staking_stake => Some(Self::ethereum__staking_stake),
|
||||
Qstr::MP_QSTR_ethereum__staking_stake_address => Some(Self::ethereum__staking_stake_address),
|
||||
Qstr::MP_QSTR_ethereum__staking_stake_intro => Some(Self::ethereum__staking_stake_intro),
|
||||
Qstr::MP_QSTR_ethereum__staking_unstake => Some(Self::ethereum__staking_unstake),
|
||||
Qstr::MP_QSTR_ethereum__staking_unstake_intro => Some(Self::ethereum__staking_unstake_intro),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -463,6 +463,7 @@ struct ConfirmBlobParams {
|
||||
info_button: bool,
|
||||
hold: bool,
|
||||
chunkify: bool,
|
||||
text_mono: bool,
|
||||
}
|
||||
|
||||
impl ConfirmBlobParams {
|
||||
@ -485,6 +486,7 @@ impl ConfirmBlobParams {
|
||||
info_button: false,
|
||||
hold,
|
||||
chunkify: false,
|
||||
text_mono: true,
|
||||
}
|
||||
}
|
||||
|
||||
@ -508,6 +510,11 @@ impl ConfirmBlobParams {
|
||||
self
|
||||
}
|
||||
|
||||
fn with_text_mono(mut self, text_mono: bool) -> Self {
|
||||
self.text_mono = text_mono;
|
||||
self
|
||||
}
|
||||
|
||||
fn into_layout(self) -> Result<Obj, Error> {
|
||||
let paragraphs = ConfirmBlob {
|
||||
description: self.description.unwrap_or_else(StrBuffer::empty),
|
||||
@ -518,8 +525,10 @@ impl ConfirmBlobParams {
|
||||
data_font: if self.chunkify {
|
||||
let data: StrBuffer = self.data.try_into()?;
|
||||
theme::get_chunkified_text_style(data.len())
|
||||
} else {
|
||||
} else if self.text_mono {
|
||||
&theme::TEXT_MONO
|
||||
} else {
|
||||
&theme::TEXT_NORMAL
|
||||
},
|
||||
}
|
||||
.into_paragraphs();
|
||||
@ -738,6 +747,7 @@ extern "C" fn new_show_info_with_cancel(n_args: usize, args: *const Obj, kwargs:
|
||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
|
||||
let horizontal: bool = kwargs.get_or(Qstr::MP_QSTR_horizontal, false)?;
|
||||
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
|
||||
|
||||
let mut paragraphs = ParagraphVecShort::new();
|
||||
|
||||
@ -746,7 +756,14 @@ extern "C" fn new_show_info_with_cancel(n_args: usize, args: *const Obj, kwargs:
|
||||
let key: StrBuffer = key.try_into()?;
|
||||
let value: StrBuffer = value.try_into()?;
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_NORMAL, key).no_break());
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_MONO, value));
|
||||
if chunkify {
|
||||
paragraphs.add(Paragraph::new(
|
||||
theme::get_chunkified_text_style(value.len()),
|
||||
value,
|
||||
));
|
||||
} else {
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_MONO, value));
|
||||
}
|
||||
}
|
||||
|
||||
let axis = match horizontal {
|
||||
@ -787,11 +804,13 @@ extern "C" fn new_confirm_value(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
.try_into_option()?;
|
||||
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
|
||||
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
|
||||
let text_mono: bool = kwargs.get_or(Qstr::MP_QSTR_text_mono, true)?;
|
||||
|
||||
ConfirmBlobParams::new(title, value, description, verb, verb_cancel, hold)
|
||||
.with_subtitle(subtitle)
|
||||
.with_info_button(info_button)
|
||||
.with_chunkify(chunkify)
|
||||
.with_text_mono(text_mono)
|
||||
.into_layout()
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -1779,6 +1798,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// title: str,
|
||||
/// items: Iterable[Tuple[str, str]],
|
||||
/// horizontal: bool = False,
|
||||
/// chunkify: bool = False,
|
||||
/// ) -> object:
|
||||
/// """Show metadata for outgoing transaction."""
|
||||
Qstr::MP_QSTR_show_info_with_cancel => obj_fn_kw!(0, new_show_info_with_cancel).as_obj(),
|
||||
@ -1794,6 +1814,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// info_button: bool = False,
|
||||
/// hold: bool = False,
|
||||
/// chunkify: bool = False,
|
||||
/// text_mono: bool = True,
|
||||
/// ) -> object:
|
||||
/// """Confirm value. Merge of confirm_total and confirm_output."""
|
||||
Qstr::MP_QSTR_confirm_value => obj_fn_kw!(0, new_confirm_value).as_obj(),
|
||||
|
@ -574,6 +574,7 @@ def show_info_with_cancel(
|
||||
title: str,
|
||||
items: Iterable[Tuple[str, str]],
|
||||
horizontal: bool = False,
|
||||
chunkify: bool = False,
|
||||
) -> object:
|
||||
"""Show metadata for outgoing transaction."""
|
||||
|
||||
@ -590,6 +591,7 @@ def confirm_value(
|
||||
info_button: bool = False,
|
||||
hold: bool = False,
|
||||
chunkify: bool = False,
|
||||
text_mono: bool = True,
|
||||
) -> object:
|
||||
"""Confirm value. Merge of confirm_total and confirm_output."""
|
||||
|
||||
|
@ -288,6 +288,14 @@ class TR:
|
||||
ethereum__show_full_message: str = "Show full message"
|
||||
ethereum__show_full_struct: str = "Show full struct"
|
||||
ethereum__sign_eip712: str = "Really sign EIP-712 typed data?"
|
||||
ethereum__staking_claim: str = "CLAIM"
|
||||
ethereum__staking_claim_address: str = "CLAIM ADDRESS"
|
||||
ethereum__staking_claim_intro: str = "Claim ETH from Everstake?"
|
||||
ethereum__staking_stake: str = "STAKE"
|
||||
ethereum__staking_stake_address: str = "STAKE ADDRESS"
|
||||
ethereum__staking_stake_intro: str = "Stake ETH on Everstake?"
|
||||
ethereum__staking_unstake: str = "UNSTAKE"
|
||||
ethereum__staking_unstake_intro: str = "Unstake ETH from Everstake?"
|
||||
ethereum__title_confirm_data: str = "CONFIRM DATA"
|
||||
ethereum__title_confirm_domain: str = "CONFIRM DOMAIN"
|
||||
ethereum__title_confirm_message: str = "CONFIRM MESSAGE"
|
||||
|
@ -535,6 +535,8 @@ if not utils.BITCOIN_ONLY:
|
||||
import apps.ethereum.sign_tx_eip1559
|
||||
apps.ethereum.sign_typed_data
|
||||
import apps.ethereum.sign_typed_data
|
||||
apps.ethereum.staking_tx_constants
|
||||
import apps.ethereum.staking_tx_constants
|
||||
apps.ethereum.tokens
|
||||
import apps.ethereum.tokens
|
||||
apps.ethereum.verify_message
|
||||
|
@ -1,14 +1,17 @@
|
||||
from typing import TYPE_CHECKING
|
||||
from ubinascii import hexlify
|
||||
|
||||
from trezor import TR
|
||||
|
||||
from . import networks
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import EthereumFieldType
|
||||
from typing import Iterable
|
||||
|
||||
from trezor.messages import EthereumFieldType, EthereumTokenInfo
|
||||
|
||||
from .networks import EthereumNetworkInfo
|
||||
|
||||
|
||||
RSKIP60_NETWORKS = (30, 31)
|
||||
|
||||
|
||||
@ -126,6 +129,72 @@ def decode_typed_data(data: bytes, type_name: str) -> str:
|
||||
raise ValueError # Unsupported data type for direct field decoding
|
||||
|
||||
|
||||
def get_fee_items_regular(
|
||||
gas_price: int, gas_limit: int, network: EthereumNetworkInfo
|
||||
) -> Iterable[tuple[str, str]]:
|
||||
# regular
|
||||
gas_limit_str = TR.ethereum__units_template.format(gas_limit)
|
||||
gas_price_str = format_ethereum_amount(
|
||||
gas_price, None, network, force_unit_gwei=True
|
||||
)
|
||||
|
||||
return (
|
||||
(TR.ethereum__gas_limit, gas_limit_str),
|
||||
(TR.ethereum__gas_price, gas_price_str),
|
||||
)
|
||||
|
||||
|
||||
def get_fee_items_eip1559(
|
||||
max_gas_fee: int,
|
||||
max_priority_fee: int,
|
||||
gas_limit: int,
|
||||
network: EthereumNetworkInfo,
|
||||
) -> Iterable[tuple[str, str]]:
|
||||
# EIP-1559
|
||||
gas_limit_str = TR.ethereum__units_template.format(gas_limit)
|
||||
max_gas_fee_str = format_ethereum_amount(
|
||||
max_gas_fee, None, network, force_unit_gwei=True
|
||||
)
|
||||
max_priority_fee_str = format_ethereum_amount(
|
||||
max_priority_fee, None, network, force_unit_gwei=True
|
||||
)
|
||||
|
||||
return (
|
||||
(TR.ethereum__gas_limit, gas_limit_str),
|
||||
(TR.ethereum__max_gas_price, max_gas_fee_str),
|
||||
(TR.ethereum__priority_fee, max_priority_fee_str),
|
||||
)
|
||||
|
||||
|
||||
def format_ethereum_amount(
|
||||
value: int,
|
||||
token: EthereumTokenInfo | None,
|
||||
network: EthereumNetworkInfo,
|
||||
force_unit_gwei: bool = False,
|
||||
) -> str:
|
||||
from trezor.strings import format_amount
|
||||
|
||||
if token:
|
||||
suffix = token.symbol
|
||||
decimals = token.decimals
|
||||
else:
|
||||
suffix = network.symbol
|
||||
decimals = 18
|
||||
|
||||
if force_unit_gwei:
|
||||
assert token is None
|
||||
assert decimals >= 9
|
||||
decimals = decimals - 9
|
||||
suffix = "Gwei"
|
||||
elif decimals > 9 and value < 10 ** (decimals - 9):
|
||||
# Don't want to display wei values for tokens with small decimal numbers
|
||||
suffix = "Wei " + suffix
|
||||
decimals = 0
|
||||
|
||||
amount = format_amount(value, decimals)
|
||||
return f"{amount} {suffix}"
|
||||
|
||||
|
||||
def _from_bytes_bigendian_signed(b: bytes) -> int:
|
||||
negative = b[0] & 0x80
|
||||
if negative:
|
||||
|
@ -4,12 +4,12 @@ from trezor import TR, ui
|
||||
from trezor.enums import ButtonRequestType
|
||||
from trezor.ui.layouts import (
|
||||
confirm_blob,
|
||||
confirm_ethereum_tx,
|
||||
confirm_ethereum_staking_tx,
|
||||
confirm_text,
|
||||
should_show_more,
|
||||
)
|
||||
|
||||
from .helpers import address_from_bytes, decode_typed_data
|
||||
from .helpers import address_from_bytes, decode_typed_data, format_ethereum_amount
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Awaitable, Iterable
|
||||
@ -25,12 +25,14 @@ if TYPE_CHECKING:
|
||||
async def require_confirm_tx(
|
||||
to_bytes: bytes,
|
||||
value: int,
|
||||
gas_price: int,
|
||||
gas_limit: int,
|
||||
maximum_fee: str,
|
||||
fee_info_items: Iterable[tuple[str, str]],
|
||||
network: EthereumNetworkInfo,
|
||||
token: EthereumTokenInfo | None,
|
||||
chunkify: bool,
|
||||
) -> None:
|
||||
from trezor.ui.layouts import confirm_ethereum_tx
|
||||
|
||||
if to_bytes:
|
||||
to_str = address_from_bytes(to_bytes, network)
|
||||
else:
|
||||
@ -38,56 +40,80 @@ async def require_confirm_tx(
|
||||
chunkify = False
|
||||
|
||||
total_amount = format_ethereum_amount(value, token, network)
|
||||
maximum_fee = format_ethereum_amount(gas_price * gas_limit, None, network)
|
||||
gas_limit_str = TR.ethereum__units_template.format(gas_limit)
|
||||
gas_price_str = format_ethereum_amount(
|
||||
gas_price, None, network, force_unit_gwei=True
|
||||
)
|
||||
|
||||
items = (
|
||||
(TR.ethereum__gas_limit, gas_limit_str),
|
||||
(TR.ethereum__gas_price, gas_price_str),
|
||||
)
|
||||
|
||||
await confirm_ethereum_tx(
|
||||
to_str, total_amount, maximum_fee, items, chunkify=chunkify
|
||||
to_str, total_amount, maximum_fee, fee_info_items, chunkify=chunkify
|
||||
)
|
||||
|
||||
|
||||
async def require_confirm_tx_eip1559(
|
||||
to_bytes: bytes,
|
||||
async def require_confirm_stake(
|
||||
addr_bytes: bytes,
|
||||
value: int,
|
||||
max_gas_fee: int,
|
||||
max_priority_fee: int,
|
||||
gas_limit: int,
|
||||
maximum_fee: str,
|
||||
fee_info_items: Iterable[tuple[str, str]],
|
||||
network: EthereumNetworkInfo,
|
||||
token: EthereumTokenInfo | None,
|
||||
chunkify: bool,
|
||||
) -> None:
|
||||
if to_bytes:
|
||||
to_str = address_from_bytes(to_bytes, network)
|
||||
else:
|
||||
to_str = TR.ethereum__new_contract
|
||||
chunkify = False
|
||||
|
||||
total_amount = format_ethereum_amount(value, token, network)
|
||||
maximum_fee = format_ethereum_amount(max_gas_fee * gas_limit, None, network)
|
||||
gas_limit_str = TR.ethereum__units_template.format(gas_limit)
|
||||
max_gas_fee_str = format_ethereum_amount(
|
||||
max_gas_fee, None, network, force_unit_gwei=True
|
||||
)
|
||||
max_priority_fee_str = format_ethereum_amount(
|
||||
max_priority_fee, None, network, force_unit_gwei=True
|
||||
addr_str = address_from_bytes(addr_bytes, network)
|
||||
total_amount = format_ethereum_amount(value, None, network)
|
||||
await confirm_ethereum_staking_tx(
|
||||
TR.ethereum__staking_stake, # title
|
||||
TR.ethereum__staking_stake_intro, # intro_question
|
||||
TR.ethereum__staking_stake, # verb
|
||||
total_amount, # total_amount
|
||||
maximum_fee, # maximum_fee
|
||||
addr_str, # address
|
||||
TR.ethereum__staking_stake_address, # address_title
|
||||
fee_info_items, # info_items
|
||||
chunkify=chunkify,
|
||||
)
|
||||
|
||||
items: tuple[tuple[str, str], ...] = (
|
||||
(TR.ethereum__gas_limit, gas_limit_str),
|
||||
(TR.ethereum__max_gas_price, max_gas_fee_str),
|
||||
(TR.ethereum__priority_fee, max_priority_fee_str),
|
||||
|
||||
async def require_confirm_unstake(
|
||||
addr_bytes: bytes,
|
||||
value: int,
|
||||
maximum_fee: str,
|
||||
fee_info_items: Iterable[tuple[str, str]],
|
||||
network: EthereumNetworkInfo,
|
||||
chunkify: bool,
|
||||
) -> None:
|
||||
|
||||
addr_str = address_from_bytes(addr_bytes, network)
|
||||
total_amount = format_ethereum_amount(value, None, network)
|
||||
|
||||
await confirm_ethereum_staking_tx(
|
||||
TR.ethereum__staking_unstake, # title
|
||||
TR.ethereum__staking_unstake_intro, # intro_question
|
||||
TR.ethereum__staking_unstake, # verb
|
||||
total_amount, # total_amount
|
||||
maximum_fee, # maximum_fee
|
||||
addr_str, # address
|
||||
TR.ethereum__staking_stake_address, # address_title
|
||||
fee_info_items, # info_items
|
||||
chunkify=chunkify,
|
||||
)
|
||||
|
||||
await confirm_ethereum_tx(
|
||||
to_str, total_amount, maximum_fee, items, chunkify=chunkify
|
||||
|
||||
async def require_confirm_claim(
|
||||
addr_bytes: bytes,
|
||||
maximum_fee: str,
|
||||
fee_info_items: Iterable[tuple[str, str]],
|
||||
network: EthereumNetworkInfo,
|
||||
chunkify: bool,
|
||||
) -> None:
|
||||
|
||||
addr_str = address_from_bytes(addr_bytes, network)
|
||||
await confirm_ethereum_staking_tx(
|
||||
TR.ethereum__staking_claim, # title
|
||||
TR.ethereum__staking_claim_intro, # intro_question
|
||||
TR.ethereum__staking_claim, # verb
|
||||
"", # total_amount
|
||||
maximum_fee, # maximum_fee
|
||||
addr_str, # address
|
||||
TR.ethereum__staking_claim_address, # address_title
|
||||
fee_info_items, # info_items
|
||||
chunkify=chunkify,
|
||||
)
|
||||
|
||||
|
||||
@ -119,7 +145,7 @@ def require_confirm_address(address_bytes: bytes) -> Awaitable[None]:
|
||||
)
|
||||
|
||||
|
||||
def require_confirm_data(data: bytes, data_total: int) -> Awaitable[None]:
|
||||
def require_confirm_other_data(data: bytes, data_total: int) -> Awaitable[None]:
|
||||
return confirm_blob(
|
||||
"confirm_data",
|
||||
TR.ethereum__title_confirm_data,
|
||||
@ -258,35 +284,6 @@ async def confirm_typed_value(
|
||||
)
|
||||
|
||||
|
||||
def format_ethereum_amount(
|
||||
value: int,
|
||||
token: EthereumTokenInfo | None,
|
||||
network: EthereumNetworkInfo,
|
||||
force_unit_gwei: bool = False,
|
||||
) -> str:
|
||||
from trezor.strings import format_amount
|
||||
|
||||
if token:
|
||||
suffix = token.symbol
|
||||
decimals = token.decimals
|
||||
else:
|
||||
suffix = network.symbol
|
||||
decimals = 18
|
||||
|
||||
if force_unit_gwei:
|
||||
assert token is None
|
||||
assert decimals >= 9
|
||||
decimals = decimals - 9
|
||||
suffix = "Gwei"
|
||||
elif decimals > 9 and value < 10 ** (decimals - 9):
|
||||
# Don't want to display wei values for tokens with small decimal numbers
|
||||
suffix = "Wei " + suffix
|
||||
decimals = 0
|
||||
|
||||
amount = format_amount(value, decimals)
|
||||
return f"{amount} {suffix}"
|
||||
|
||||
|
||||
def limit_str(s: str, limit: int = 16) -> str:
|
||||
"""Shortens string to show the last <limit> characters."""
|
||||
if len(s) <= limit + 2:
|
||||
|
@ -2,13 +2,23 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from trezor.crypto import rlp
|
||||
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 .helpers import bytes_from_address
|
||||
from .keychain import with_keychain_from_chain_id
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import EthereumSignTx, EthereumTokenInfo, EthereumTxAck
|
||||
from typing import Iterable
|
||||
|
||||
from trezor.messages import (
|
||||
EthereumNetworkInfo,
|
||||
EthereumSignTx,
|
||||
EthereumTokenInfo,
|
||||
EthereumTxAck,
|
||||
)
|
||||
|
||||
from apps.common.keychain import Keychain
|
||||
|
||||
@ -33,7 +43,9 @@ async def sign_tx(
|
||||
|
||||
from apps.common import paths
|
||||
|
||||
from .layout import require_confirm_data, require_confirm_tx
|
||||
from .helpers import format_ethereum_amount, get_fee_items_regular
|
||||
|
||||
data_total = msg.data_length # local_cache_attribute
|
||||
|
||||
# check
|
||||
if msg.tx_type not in [1, 6, None]:
|
||||
@ -42,26 +54,20 @@ async def sign_tx(
|
||||
raise DataError("Fee overflow")
|
||||
check_common_fields(msg)
|
||||
|
||||
# have a user confirm signing
|
||||
await paths.validate_path(keychain, msg.address_n)
|
||||
|
||||
# Handle ERC20s
|
||||
token, address_bytes, recipient, value = await handle_erc20(msg, defs)
|
||||
|
||||
data_total = msg.data_length # local_cache_attribute
|
||||
|
||||
if token is None and data_total > 0:
|
||||
await require_confirm_data(msg.data_initial_chunk, data_total)
|
||||
|
||||
await require_confirm_tx(
|
||||
recipient,
|
||||
value,
|
||||
int.from_bytes(msg.gas_price, "big"),
|
||||
int.from_bytes(msg.gas_limit, "big"),
|
||||
address_bytes = bytes_from_address(msg.to)
|
||||
gas_price = int.from_bytes(msg.gas_price, "big")
|
||||
gas_limit = int.from_bytes(msg.gas_limit, "big")
|
||||
maximum_fee = format_ethereum_amount(gas_price * gas_limit, None, defs.network)
|
||||
fee_items = get_fee_items_regular(
|
||||
gas_price,
|
||||
gas_limit,
|
||||
defs.network,
|
||||
token,
|
||||
bool(msg.chunkify),
|
||||
)
|
||||
await confirm_tx_data(msg, defs, address_bytes, maximum_fee, fee_items, data_total)
|
||||
|
||||
# sign
|
||||
data = bytearray()
|
||||
data += msg.data_initial_chunk
|
||||
data_left = data_total - len(msg.data_initial_chunk)
|
||||
@ -99,16 +105,89 @@ async def sign_tx(
|
||||
return result
|
||||
|
||||
|
||||
async def handle_erc20(
|
||||
async def confirm_tx_data(
|
||||
msg: MsgInSignTx,
|
||||
defs: Definitions,
|
||||
address_bytes: bytes,
|
||||
maximum_fee: str,
|
||||
fee_items: Iterable[tuple[str, str]],
|
||||
data_total_len: int,
|
||||
) -> None:
|
||||
# function distinguishes between staking / smart contracts / regular transactions
|
||||
from .layout import 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)
|
||||
|
||||
if token is None and data_total_len > 0:
|
||||
await require_confirm_other_data(msg.data_initial_chunk, data_total_len)
|
||||
|
||||
await require_confirm_tx(
|
||||
recipient,
|
||||
value,
|
||||
maximum_fee,
|
||||
fee_items,
|
||||
defs.network,
|
||||
token,
|
||||
bool(msg.chunkify),
|
||||
)
|
||||
|
||||
|
||||
async def handle_staking(
|
||||
msg: MsgInSignTx,
|
||||
network: EthereumNetworkInfo,
|
||||
address_bytes: bytes,
|
||||
maximum_fee: str,
|
||||
fee_items: Iterable[tuple[str, str]],
|
||||
) -> bool:
|
||||
|
||||
data_reader = BufferReader(msg.data_initial_chunk)
|
||||
if data_reader.remaining_count() < constants.SC_FUNC_SIG_BYTES:
|
||||
return False
|
||||
|
||||
func_sig = data_reader.read_memoryview(constants.SC_FUNC_SIG_BYTES)
|
||||
if address_bytes in constants.ADDRESSES_POOL:
|
||||
if func_sig == constants.SC_FUNC_SIG_STAKE:
|
||||
await _handle_staking_tx_stake(
|
||||
data_reader, msg, network, address_bytes, maximum_fee, fee_items
|
||||
)
|
||||
return True
|
||||
if func_sig == constants.SC_FUNC_SIG_UNSTAKE:
|
||||
await _handle_staking_tx_unstake(
|
||||
data_reader, msg, network, address_bytes, maximum_fee, fee_items
|
||||
)
|
||||
return True
|
||||
|
||||
if address_bytes in constants.ADDRESSES_ACCOUNTING:
|
||||
if func_sig == constants.SC_FUNC_SIG_CLAIM:
|
||||
await _handle_staking_tx_claim(
|
||||
data_reader,
|
||||
address_bytes,
|
||||
maximum_fee,
|
||||
fee_items,
|
||||
network,
|
||||
bool(msg.chunkify),
|
||||
)
|
||||
return True
|
||||
|
||||
# data not corresponding to staking transaction
|
||||
return False
|
||||
|
||||
|
||||
async def handle_erc20_transfer(
|
||||
msg: MsgInSignTx,
|
||||
definitions: Definitions,
|
||||
) -> tuple[EthereumTokenInfo | None, bytes, bytes, int]:
|
||||
address_bytes: bytes,
|
||||
) -> tuple[EthereumTokenInfo | None, bytes, int]:
|
||||
from . import tokens
|
||||
from .layout import require_confirm_unknown_token
|
||||
|
||||
data_initial_chunk = msg.data_initial_chunk # local_cache_attribute
|
||||
token = None
|
||||
address_bytes = recipient = bytes_from_address(msg.to)
|
||||
recipient = address_bytes
|
||||
value = int.from_bytes(msg.value, "big")
|
||||
if (
|
||||
len(msg.to) in (40, 42)
|
||||
@ -125,7 +204,7 @@ async def handle_erc20(
|
||||
if token is tokens.UNKNOWN_TOKEN:
|
||||
await require_confirm_unknown_token(address_bytes)
|
||||
|
||||
return token, address_bytes, recipient, value
|
||||
return token, recipient, value
|
||||
|
||||
|
||||
def _get_total_length(msg: EthereumSignTx, data_total: int) -> int:
|
||||
@ -208,3 +287,92 @@ def check_common_fields(msg: MsgInSignTx) -> None:
|
||||
|
||||
if msg.chain_id == 0:
|
||||
raise DataError("Chain ID out of bounds")
|
||||
|
||||
|
||||
async def _handle_staking_tx_stake(
|
||||
data_reader: BufferReader,
|
||||
msg: MsgInSignTx,
|
||||
network: EthereumNetworkInfo,
|
||||
address_bytes: bytes,
|
||||
maximum_fee: str,
|
||||
fee_items: Iterable[tuple[str, str]],
|
||||
) -> None:
|
||||
from .layout import require_confirm_stake
|
||||
|
||||
# stake args:
|
||||
# - arg0: uint64, source (should be 1)
|
||||
try:
|
||||
source = int.from_bytes(
|
||||
data_reader.read_memoryview(constants.SC_ARGUMENT_BYTES), "big"
|
||||
)
|
||||
if source != 1:
|
||||
raise ValueError # wrong value of 1st argument ('source' should be 1)
|
||||
if data_reader.remaining_count() != 0:
|
||||
raise ValueError # wrong number of arguments for stake (should be 1)
|
||||
except (ValueError, EOFError):
|
||||
raise DataError("Invalid staking transaction call")
|
||||
|
||||
await require_confirm_stake(
|
||||
address_bytes,
|
||||
int.from_bytes(msg.value, "big"),
|
||||
maximum_fee,
|
||||
fee_items,
|
||||
network,
|
||||
bool(msg.chunkify),
|
||||
)
|
||||
|
||||
|
||||
async def _handle_staking_tx_unstake(
|
||||
data_reader: BufferReader,
|
||||
msg: MsgInSignTx,
|
||||
network: EthereumNetworkInfo,
|
||||
address_bytes: bytes,
|
||||
maximum_fee: str,
|
||||
fee_items: Iterable[tuple[str, str]],
|
||||
) -> None:
|
||||
from .layout import require_confirm_unstake
|
||||
|
||||
# unstake args:
|
||||
# - arg0: uint256, value
|
||||
# - arg1: uint16, isAllowedInterchange (bool)
|
||||
# - arg2: uint64, source, should be 1
|
||||
try:
|
||||
value = int.from_bytes(
|
||||
data_reader.read_memoryview(constants.SC_ARGUMENT_BYTES), "big"
|
||||
)
|
||||
_ = data_reader.read_memoryview(constants.SC_ARGUMENT_BYTES) # skip arg1
|
||||
source = int.from_bytes(
|
||||
data_reader.read_memoryview(constants.SC_ARGUMENT_BYTES), "big"
|
||||
)
|
||||
if source != 1:
|
||||
raise ValueError # wrong value of 3rd argument ('source' should be 1)
|
||||
if data_reader.remaining_count() != 0:
|
||||
raise ValueError # wrong number of arguments for unstake (should be 3)
|
||||
except (ValueError, EOFError):
|
||||
raise DataError("Invalid staking transaction call")
|
||||
|
||||
await require_confirm_unstake(
|
||||
address_bytes,
|
||||
value,
|
||||
maximum_fee,
|
||||
fee_items,
|
||||
network,
|
||||
bool(msg.chunkify),
|
||||
)
|
||||
|
||||
|
||||
async def _handle_staking_tx_claim(
|
||||
data_reader: BufferReader,
|
||||
staking_addr: bytes,
|
||||
maximum_fee: str,
|
||||
fee_items: Iterable[tuple[str, str]],
|
||||
network: EthereumNetworkInfo,
|
||||
chunkify: bool,
|
||||
) -> None:
|
||||
from .layout import require_confirm_claim
|
||||
|
||||
# claim has no args
|
||||
if data_reader.remaining_count() != 0:
|
||||
raise DataError("Invalid staking transaction call")
|
||||
|
||||
await require_confirm_claim(staking_addr, maximum_fee, fee_items, network, chunkify)
|
||||
|
@ -42,8 +42,8 @@ async def sign_tx_eip1559(
|
||||
|
||||
from apps.common import paths
|
||||
|
||||
from .layout import require_confirm_data, require_confirm_tx_eip1559
|
||||
from .sign_tx import check_common_fields, handle_erc20, send_request_chunk
|
||||
from .helpers import format_ethereum_amount, get_fee_items_eip1559
|
||||
from .sign_tx import check_common_fields, confirm_tx_data, send_request_chunk
|
||||
|
||||
gas_limit = msg.gas_limit # local_cache_attribute
|
||||
data_total = msg.data_length # local_cache_attribute
|
||||
@ -55,25 +55,23 @@ async def sign_tx_eip1559(
|
||||
raise wire.DataError("Fee overflow")
|
||||
check_common_fields(msg)
|
||||
|
||||
# have a user confirm signing
|
||||
await paths.validate_path(keychain, msg.address_n)
|
||||
address_bytes = bytes_from_address(msg.to)
|
||||
|
||||
# Handle ERC20s
|
||||
token, address_bytes, recipient, value = await handle_erc20(msg, defs)
|
||||
|
||||
if token is None and data_total > 0:
|
||||
await require_confirm_data(msg.data_initial_chunk, data_total)
|
||||
|
||||
await require_confirm_tx_eip1559(
|
||||
recipient,
|
||||
value,
|
||||
int.from_bytes(msg.max_gas_fee, "big"),
|
||||
int.from_bytes(msg.max_priority_fee, "big"),
|
||||
int.from_bytes(gas_limit, "big"),
|
||||
max_gas_fee = int.from_bytes(msg.max_gas_fee, "big")
|
||||
max_priority_fee = int.from_bytes(msg.max_priority_fee, "big")
|
||||
gas_limit = int.from_bytes(msg.gas_limit, "big")
|
||||
maximum_fee = format_ethereum_amount(max_gas_fee * gas_limit, None, defs.network)
|
||||
fee_items = get_fee_items_eip1559(
|
||||
max_gas_fee,
|
||||
max_priority_fee,
|
||||
gas_limit,
|
||||
defs.network,
|
||||
token,
|
||||
bool(msg.chunkify),
|
||||
)
|
||||
await confirm_tx_data(msg, defs, address_bytes, maximum_fee, fee_items, data_total)
|
||||
|
||||
# transaction data confirmed, proceed with signing
|
||||
data = bytearray()
|
||||
data += msg.data_initial_chunk
|
||||
data_left = data_total - len(msg.data_initial_chunk)
|
||||
|
21
core/src/apps/ethereum/staking_tx_constants.py
Normal file
21
core/src/apps/ethereum/staking_tx_constants.py
Normal file
@ -0,0 +1,21 @@
|
||||
from micropython import const
|
||||
from ubinascii import unhexlify
|
||||
|
||||
# smart contract 'data' field lengths in bytes
|
||||
SC_FUNC_SIG_BYTES = const(4)
|
||||
SC_ARGUMENT_BYTES = const(32)
|
||||
|
||||
# staking operations function signatures
|
||||
SC_FUNC_SIG_STAKE = unhexlify("3a29dbae")
|
||||
SC_FUNC_SIG_UNSTAKE = unhexlify("76ec871c")
|
||||
SC_FUNC_SIG_CLAIM = unhexlify("33986ffa")
|
||||
|
||||
# addresses for pool (stake/unstake) and accounting (claim) operations
|
||||
ADDRESSES_POOL = (
|
||||
unhexlify("AFA848357154a6a624686b348303EF9a13F63264"), # holesky testnet
|
||||
unhexlify("D523794C879D9eC028960a231F866758e405bE34"), # mainnet
|
||||
)
|
||||
ADDRESSES_ACCOUNTING = (
|
||||
unhexlify("624087DD1904ab122A32878Ce9e933C7071F53B9"), # holesky testnet
|
||||
unhexlify("7a7f0b3c23C23a31cFcb0c44709be70d4D545c6e"), # mainnet
|
||||
)
|
@ -1023,7 +1023,7 @@ async def confirm_value(
|
||||
title=info_title.upper(),
|
||||
action=info_value,
|
||||
description=description,
|
||||
verb=TR.buttons__back,
|
||||
verb="",
|
||||
verb_cancel="<",
|
||||
hold=False,
|
||||
reverse=False,
|
||||
@ -1067,6 +1067,56 @@ async def confirm_total(
|
||||
)
|
||||
|
||||
|
||||
async def confirm_ethereum_staking_tx(
|
||||
title: str,
|
||||
intro_question: str,
|
||||
verb: str,
|
||||
total_amount: str,
|
||||
maximum_fee: str,
|
||||
address: str,
|
||||
address_title: str,
|
||||
info_items: Iterable[tuple[str, str]],
|
||||
br_type: str = "confirm_ethereum_staking_tx",
|
||||
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||
chunkify: bool = False,
|
||||
) -> None:
|
||||
|
||||
# intro
|
||||
await confirm_value(
|
||||
title,
|
||||
intro_question,
|
||||
"",
|
||||
br_type,
|
||||
br_code,
|
||||
verb=verb,
|
||||
info_items=((address_title, address),),
|
||||
)
|
||||
|
||||
# confirmation
|
||||
if verb == TR.ethereum__staking_claim:
|
||||
amount_title = verb
|
||||
amount_value = ""
|
||||
else:
|
||||
amount_title = TR.words__amount + ":"
|
||||
amount_value = total_amount
|
||||
await raise_if_not_confirmed(
|
||||
interact(
|
||||
RustLayout(
|
||||
trezorui2.altcoin_tx_summary(
|
||||
amount_title=amount_title,
|
||||
amount_value=amount_value,
|
||||
fee_title=TR.send__maximum_fee,
|
||||
fee_value=maximum_fee,
|
||||
items=info_items,
|
||||
cancel_cross=True,
|
||||
)
|
||||
),
|
||||
br_type=br_type,
|
||||
br_code=br_code,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def confirm_solana_tx(
|
||||
amount: str,
|
||||
fee: str,
|
||||
|
@ -859,6 +859,7 @@ def confirm_value(
|
||||
verb: str | None = None,
|
||||
subtitle: str | None = None,
|
||||
hold: bool = False,
|
||||
value_text_mono: bool = True,
|
||||
info_items: Iterable[tuple[str, str]] | None = None,
|
||||
) -> Awaitable[None]:
|
||||
"""General confirmation dialog, used by many other confirm_* functions."""
|
||||
@ -948,7 +949,11 @@ async def confirm_total(
|
||||
info_items.append((TR.confirm_total__fee_rate, fee_rate_amount))
|
||||
|
||||
await confirm_summary(
|
||||
items, TR.words__title_summary, info_items, br_type=br_type, br_code=br_code
|
||||
items,
|
||||
TR.words__title_summary,
|
||||
info_items=info_items,
|
||||
br_type=br_type,
|
||||
br_code=br_code,
|
||||
)
|
||||
|
||||
|
||||
@ -956,6 +961,7 @@ async def confirm_summary(
|
||||
items: Iterable[tuple[str, str]],
|
||||
title: str | None = None,
|
||||
info_items: Iterable[tuple[str, str]] | None = None,
|
||||
info_title: str | None = None,
|
||||
br_type: str = "confirm_total",
|
||||
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||
) -> None:
|
||||
@ -971,7 +977,7 @@ async def confirm_summary(
|
||||
info_items = info_items or []
|
||||
info_layout = RustLayout(
|
||||
trezorui2.show_info_with_cancel(
|
||||
title=TR.words__title_information,
|
||||
title=info_title.upper() if info_title else TR.words__title_information,
|
||||
items=info_items,
|
||||
)
|
||||
)
|
||||
@ -1025,6 +1031,60 @@ async def confirm_ethereum_tx(
|
||||
continue
|
||||
|
||||
|
||||
async def confirm_ethereum_staking_tx(
|
||||
title: str,
|
||||
intro_question: str,
|
||||
verb: str,
|
||||
total_amount: str,
|
||||
maximum_fee: str,
|
||||
address: str,
|
||||
address_title: str,
|
||||
info_items: Iterable[tuple[str, str]] | None = None,
|
||||
chunkify: bool = False,
|
||||
br_type: str = "confirm_ethereum_staking_tx",
|
||||
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||
) -> None:
|
||||
|
||||
# intro
|
||||
# NOTE: this layout very similar to `confirm_value` with some adjustments
|
||||
msg_layout = RustLayout(
|
||||
trezorui2.confirm_value(
|
||||
title=title,
|
||||
value=intro_question,
|
||||
description=None,
|
||||
subtitle=None,
|
||||
verb=verb,
|
||||
info_button=True,
|
||||
text_mono=False,
|
||||
)
|
||||
)
|
||||
info_layout = RustLayout(
|
||||
trezorui2.show_info_with_cancel(
|
||||
title=address_title,
|
||||
items=(("", address),),
|
||||
chunkify=chunkify,
|
||||
)
|
||||
)
|
||||
await raise_if_not_confirmed(with_info(msg_layout, info_layout, br_type, br_code))
|
||||
|
||||
# confirmation
|
||||
if verb == TR.ethereum__staking_claim:
|
||||
items = ((TR.send__maximum_fee, maximum_fee),)
|
||||
else:
|
||||
items = (
|
||||
(TR.words__amount + ":", total_amount),
|
||||
(TR.send__maximum_fee, maximum_fee),
|
||||
)
|
||||
await confirm_summary(
|
||||
items, # items
|
||||
title=title,
|
||||
info_title=TR.confirm_total__title_fee,
|
||||
info_items=info_items,
|
||||
br_type=br_type,
|
||||
br_code=br_code,
|
||||
)
|
||||
|
||||
|
||||
async def confirm_solana_tx(
|
||||
amount: str,
|
||||
fee: str,
|
||||
|
@ -4,12 +4,11 @@ if not utils.BITCOIN_ONLY:
|
||||
from ethereum_common import make_network, make_token
|
||||
|
||||
from apps.ethereum import networks
|
||||
from apps.ethereum.layout import format_ethereum_amount
|
||||
from apps.ethereum.helpers import format_ethereum_amount
|
||||
from apps.ethereum.tokens import UNKNOWN_TOKEN
|
||||
|
||||
ETH = networks.by_chain_id(1)
|
||||
|
||||
|
||||
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
||||
class TestFormatEthereumAmount(unittest.TestCase):
|
||||
def test_denominations(self):
|
||||
|
@ -299,6 +299,14 @@
|
||||
"ethereum__units_template": "{} units",
|
||||
"ethereum__unknown_token": "Unknown token",
|
||||
"ethereum__valid_signature": "The signature is valid.",
|
||||
"ethereum__staking_stake": "STAKE",
|
||||
"ethereum__staking_stake_address": "STAKE ADDRESS",
|
||||
"ethereum__staking_stake_intro": "Stake ETH on Everstake?",
|
||||
"ethereum__staking_unstake": "UNSTAKE",
|
||||
"ethereum__staking_unstake_intro": "Unstake ETH from Everstake?",
|
||||
"ethereum__staking_claim": "CLAIM",
|
||||
"ethereum__staking_claim_address": "CLAIM ADDRESS",
|
||||
"ethereum__staking_claim_intro": "Claim ETH from Everstake?",
|
||||
"experimental_mode__enable": "Enable experimental features?",
|
||||
"experimental_mode__only_for_dev": "Only for development and beta testing!",
|
||||
"experimental_mode__title": "EXPERIMENTAL MODE",
|
||||
|
@ -832,5 +832,13 @@
|
||||
"830": "words__writable",
|
||||
"831": "words__yes",
|
||||
"832": "reboot_to_bootloader__just_a_moment",
|
||||
"833": "inputs__previous"
|
||||
"833": "inputs__previous",
|
||||
"834": "ethereum__staking_claim",
|
||||
"835": "ethereum__staking_claim_address",
|
||||
"836": "ethereum__staking_claim_intro",
|
||||
"837": "ethereum__staking_stake",
|
||||
"838": "ethereum__staking_stake_address",
|
||||
"839": "ethereum__staking_stake_intro",
|
||||
"840": "ethereum__staking_unstake",
|
||||
"841": "ethereum__staking_unstake_intro"
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"current": {
|
||||
"merkle_root": "4add9b7a2b80544a382378bc1abdae38600460825ef8010d45da5c2f28d86d26",
|
||||
"merkle_root": "ebba747f556487944a26b19deb5910648694670f0de05f5a1569e1e12cf47ea0",
|
||||
"signature": null,
|
||||
"datetime": "2024-02-21T09:03:23.136322",
|
||||
"commit": "1dc00561ae04804aecbb0715d092c2a907e8eed8"
|
||||
"datetime": "2024-02-21T15:09:10.231125",
|
||||
"commit": "4204ba14044132269dc708e3f0b0cfac3bbfd906"
|
||||
},
|
||||
"history": []
|
||||
}
|
||||
|
@ -454,3 +454,74 @@ def test_signtx_data_pagination(client: Client, flow):
|
||||
client.watch_layout()
|
||||
client.set_input_flow(flow(client, cancel=True))
|
||||
_sign_tx_call()
|
||||
|
||||
|
||||
@pytest.mark.skip_t1("T1 does not support Everstake")
|
||||
@parametrize_using_common_fixtures("ethereum/sign_tx_staking.json")
|
||||
# TODO input flows to go into info screens - then also parametrizing chunkify might make sense
|
||||
# @pytest.mark.parametrize("chunkify", (True, False))
|
||||
def test_signtx_staking(client: Client, parameters: dict, result: dict):
|
||||
with client:
|
||||
sig_v, sig_r, sig_s = ethereum.sign_tx(
|
||||
client,
|
||||
n=parse_path(parameters["path"]),
|
||||
nonce=int(parameters["nonce"], 16),
|
||||
gas_price=int(parameters["gas_price"], 16),
|
||||
gas_limit=int(parameters["gas_limit"], 16),
|
||||
to=parameters["to_address"],
|
||||
value=int(parameters["value"], 16),
|
||||
data=bytes.fromhex(parameters["data"]),
|
||||
chain_id=parameters["chain_id"],
|
||||
tx_type=parameters["tx_type"],
|
||||
definitions=None,
|
||||
chunkify=False,
|
||||
)
|
||||
expected_v = 2 * parameters["chain_id"] + 35
|
||||
assert sig_v in (expected_v, expected_v + 1)
|
||||
assert sig_r.hex() == result["sig_r"]
|
||||
assert sig_s.hex() == result["sig_s"]
|
||||
assert sig_v == result["sig_v"]
|
||||
|
||||
|
||||
@pytest.mark.skip_t1("T1 does not support Everstake")
|
||||
@parametrize_using_common_fixtures("ethereum/sign_tx_staking_data_error.json")
|
||||
def test_signtx_staking_bad_inputs(client: Client, parameters: dict, result: dict):
|
||||
# result not needed
|
||||
with pytest.raises(TrezorFailure, match=r"DataError"):
|
||||
ethereum.sign_tx(
|
||||
client,
|
||||
n=parse_path(parameters["path"]),
|
||||
nonce=int(parameters["nonce"], 16),
|
||||
gas_price=int(parameters["gas_price"], 16),
|
||||
gas_limit=int(parameters["gas_limit"], 16),
|
||||
to=parameters["to_address"],
|
||||
value=int(parameters["value"], 16),
|
||||
data=bytes.fromhex(parameters["data"]),
|
||||
chain_id=parameters["chain_id"],
|
||||
tx_type=parameters["tx_type"],
|
||||
definitions=None,
|
||||
chunkify=False,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip_t1("T1 does not support Everstake")
|
||||
@parametrize_using_common_fixtures("ethereum/sign_tx_staking_eip1559.json")
|
||||
def test_signtx_staking_eip1559(client: Client, parameters: dict, result: dict):
|
||||
with client:
|
||||
sig_v, sig_r, sig_s = ethereum.sign_tx_eip1559(
|
||||
client,
|
||||
n=parse_path(parameters["path"]),
|
||||
nonce=int(parameters["nonce"], 16),
|
||||
max_gas_fee=int(parameters["max_gas_fee"], 16),
|
||||
max_priority_fee=int(parameters["max_priority_fee"], 16),
|
||||
gas_limit=int(parameters["gas_limit"], 16),
|
||||
to=parameters["to_address"],
|
||||
value=int(parameters["value"], 16),
|
||||
data=bytes.fromhex(parameters["data"]),
|
||||
chain_id=parameters["chain_id"],
|
||||
definitions=None,
|
||||
chunkify=True,
|
||||
)
|
||||
assert sig_r.hex() == result["sig_r"]
|
||||
assert sig_s.hex() == result["sig_s"]
|
||||
assert sig_v == result["sig_v"]
|
||||
|
@ -4507,6 +4507,23 @@
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_eip1559_access_list_larger": "5ec441ee292a9034c7d859f216050e7af702dcc219ed16e4ca17352ae4784c9b",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_fee_info": "b4ae728ff71c1e6112abbb0111b85b2760f957b677726b35734e63c318495408",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_go_back_from_summary": "263993daffe2a77a46a17d5b598aca84de52ba0e051e4cb5de5c524a48192ed3",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking[claim_holesky]": "8381fbc4aac0431757244c7813c33abee1a3457661a71b7b8b71fb4cd319d6f8",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking[claim_mainnet]": "8381fbc4aac0431757244c7813c33abee1a3457661a71b7b8b71fb4cd319d6f8",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking[stake_holesky]": "856bc0275109dcf0fbac25b20916ac78f358533e41e896b299959fbc0364a240",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking[stake_main]": "856bc0275109dcf0fbac25b20916ac78f358533e41e896b299959fbc0364a240",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking[unstake_holesky]": "4d80581b69013423285c924d837135a2c2b09b62cf2635fdbe3a8f224511bc7e",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking[unstake_main]": "4d80581b69013423285c924d837135a2c2b09b62cf2635fdbe3a8f224511bc7e",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking_bad_inputs[claim_bad_inputs_1]": "b70d9d2aa7a8ace3251763c1d2fcb53dd8c741b7520d717398df8f7ff8ac9128",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking_bad_inputs[stake_bad_inputs_1]": "b70d9d2aa7a8ace3251763c1d2fcb53dd8c741b7520d717398df8f7ff8ac9128",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking_bad_inputs[stake_bad_inputs_2]": "b70d9d2aa7a8ace3251763c1d2fcb53dd8c741b7520d717398df8f7ff8ac9128",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking_bad_inputs[unstake_bad_inputs_1]": "b70d9d2aa7a8ace3251763c1d2fcb53dd8c741b7520d717398df8f7ff8ac9128",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking_bad_inputs[unstake_bad_inputs_2]": "b70d9d2aa7a8ace3251763c1d2fcb53dd8c741b7520d717398df8f7ff8ac9128",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking_eip1559[claim_holesky]": "bf6067c40a106b593ecd43632308fd384a505be4b10269eae07090d1bb24b3f1",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking_eip1559[claim_mainnet]": "bf6067c40a106b593ecd43632308fd384a505be4b10269eae07090d1bb24b3f1",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking_eip1559[stake_holesky]": "5f0472c1ae8e221509b5568a89f7942e18caaa8e7b022d75519cfa0e671c94bc",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking_eip1559[stake_main]": "5f0472c1ae8e221509b5568a89f7942e18caaa8e7b022d75519cfa0e671c94bc",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking_eip1559[unstake_holesky]": "9c0d8add3295ed7de71bc13084e1216149af5cce259a72dc9c558c4fbfc9e479",
|
||||
"TR_en_ethereum-test_signtx.py::test_signtx_staking_eip1559[unstake_main]": "9c0d8add3295ed7de71bc13084e1216149af5cce259a72dc9c558c4fbfc9e479",
|
||||
"TR_en_misc-test_cosi.py::test_cosi_different_key": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095",
|
||||
"TR_en_misc-test_cosi.py::test_cosi_nonce": "df3420ca2395ced6fb2e3e5b984ece9d1a1151d877061681582c8f9404416600",
|
||||
"TR_en_misc-test_cosi.py::test_cosi_pubkey": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095",
|
||||
@ -11564,6 +11581,23 @@
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_eip1559_access_list_larger": "243010310ac5a4c70c627507ea8501cc61c2e20728eb06bc796f093132bebb4f",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_fee_info": "714e4c5f6e6b45fa3e78f74c7ee5e3332f39686f8b708a4f56232105bde0c3e4",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_go_back_from_summary": "8bc38a773c40a70c1eb9b91a5d02ce0a61591ce9e42bd0073bc1395f560f2490",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking[claim_holesky]": "63a2b20a46d7eb9dbe188f45286f0e19b696b4fa072743156a1f70b8c33d5dad",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking[claim_mainnet]": "63a2b20a46d7eb9dbe188f45286f0e19b696b4fa072743156a1f70b8c33d5dad",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking[stake_holesky]": "f24ba4c504e12ec403aa99f19f9d9c78cc513edb2b7671063033902d089d894c",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking[stake_main]": "f24ba4c504e12ec403aa99f19f9d9c78cc513edb2b7671063033902d089d894c",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking[unstake_holesky]": "b24d5247a866e3aa69fe3fc17eabaa210890b285e3c4b84eb253570fcc0c8bed",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking[unstake_main]": "b24d5247a866e3aa69fe3fc17eabaa210890b285e3c4b84eb253570fcc0c8bed",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking_bad_inputs[claim_bad_inputs_1]": "3b6c5cf5c6512f1491b77f895d21d2f850f774c2b9d67c1b76eaeb2892e95e6b",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking_bad_inputs[stake_bad_inputs_1]": "3b6c5cf5c6512f1491b77f895d21d2f850f774c2b9d67c1b76eaeb2892e95e6b",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking_bad_inputs[stake_bad_inputs_2]": "3b6c5cf5c6512f1491b77f895d21d2f850f774c2b9d67c1b76eaeb2892e95e6b",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking_bad_inputs[unstake_bad_inputs_1]": "3b6c5cf5c6512f1491b77f895d21d2f850f774c2b9d67c1b76eaeb2892e95e6b",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking_bad_inputs[unstake_bad_inputs_2]": "3b6c5cf5c6512f1491b77f895d21d2f850f774c2b9d67c1b76eaeb2892e95e6b",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking_eip1559[claim_holesky]": "63a2b20a46d7eb9dbe188f45286f0e19b696b4fa072743156a1f70b8c33d5dad",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking_eip1559[claim_mainnet]": "63a2b20a46d7eb9dbe188f45286f0e19b696b4fa072743156a1f70b8c33d5dad",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking_eip1559[stake_holesky]": "f24ba4c504e12ec403aa99f19f9d9c78cc513edb2b7671063033902d089d894c",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking_eip1559[stake_main]": "f24ba4c504e12ec403aa99f19f9d9c78cc513edb2b7671063033902d089d894c",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking_eip1559[unstake_holesky]": "b24d5247a866e3aa69fe3fc17eabaa210890b285e3c4b84eb253570fcc0c8bed",
|
||||
"TT_en_ethereum-test_signtx.py::test_signtx_staking_eip1559[unstake_main]": "b24d5247a866e3aa69fe3fc17eabaa210890b285e3c4b84eb253570fcc0c8bed",
|
||||
"TT_en_misc-test_cosi.py::test_cosi_different_key": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3",
|
||||
"TT_en_misc-test_cosi.py::test_cosi_nonce": "25a47ec1384fb563a6495d92d9319d19220cbb15b0f33fbdc26f01d3ccde1980",
|
||||
"TT_en_misc-test_cosi.py::test_cosi_pubkey": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3",
|
||||
|
@ -6,7 +6,7 @@ EXCEPTIONS+=( "decred" ) # "decred" figures in field names used by the bitcoin
|
||||
EXCEPTIONS+=( "omni" ) # OMNI is part of the bitcoin app
|
||||
# BIP39 or SLIP39 words that have "dash" in them
|
||||
EXCEPTIONS+=( "dash" )
|
||||
EXCEPTIONS+=( "confirm_ethereum_tx" ) # is model-specific, so is in layout/__init__.py instead of ethereum/layout.py
|
||||
EXCEPTIONS+=( "confirm_ethereum_tx" "confirm_ethereum_staking_tx" ) # model-specific, so is in layout/__init__.py instead of ethereum/layout.py
|
||||
EXCEPTIONS+=( "__" ) # ignoring the translations blob (section__key delimiter)
|
||||
EXCEPTIONS+=( "{}" ) # ignoring the translations blob (template identifier)
|
||||
|
||||
|
2
vendor/fido2-tests
vendored
2
vendor/fido2-tests
vendored
@ -1 +1 @@
|
||||
Subproject commit 9cfd22ef20fec2c34d0f0e5c16a5d5152da30861
|
||||
Subproject commit 28e177c4424820aee8a6f031474c890e5bafe72c
|
Loading…
Reference in New Issue
Block a user