From 533278e2c40ee764ecb87d4430fa6650f251ff0c Mon Sep 17 00:00:00 2001 From: Alexandre Chabrolin <9203826+chabroA@users.noreply.github.com> Date: Tue, 12 Sep 2023 12:57:32 +0200 Subject: [PATCH] [LIVE-5135] Migrate Ethereum family implementation to EVM family (#4285) Co-authored-by: lvndry Co-authored-by: Alexandre Chabrolin <9203826+chabroA@users.noreply.github.com> Co-authored-by: Landry Monga --- .changeset/small-jars-hug.md | 16 + .../workflows/bot-staging-explorer-eth.yml | 4 +- apps/cli/src/live-common-setup-base.ts | 2 - apps/ledger-live-desktop/package.json | 1 - apps/ledger-live-desktop/src/config/urls.ts | 2 + .../live-common-set-supported-currencies.ts | 2 - .../src/renderer/actions/swap.ts | 20 +- .../OperationsList/EditOperationPanel.tsx | 23 +- .../src/renderer/components/SendFeeMode.tsx | 1 + .../components/SignMessageConfirm/index.tsx | 7 +- .../src/renderer/components/Stepper/index.tsx | 4 +- .../components/TransactionConfirm/index.tsx | 1 + .../drawers/OperationDetails/index.tsx | 70 +- .../families/ethereum/AccountBodyHeader.ts | 1 - .../EditStuckTransactionPanelBodyHeader.tsx | 36 - .../ethereum/EditTransactionModal/Body.tsx | 272 -- .../ethereum/EditTransactionModal/index.tsx | 50 - .../EditTransactionModal/steps/StepFees.tsx | 174 - .../EditTransactionModal/steps/StepMethod.tsx | 237 -- .../steps/StepSummaryFooter.tsx | 70 - .../ethereum/EditTransactionModal/types.ts | 41 - .../families/ethereum/GasLimitField.tsx | 101 - .../families/ethereum/GasPriceField.tsx | 73 - .../families/ethereum/MaxFeeField.tsx | 158 - .../families/ethereum/PriorityFeeField.tsx | 184 - .../families/ethereum/SelectFeeStrategy.tsx | 239 -- .../families/ethereum/SendAmountFields.tsx | 69 - .../src/renderer/families/ethereum/helpers.ts | 37 - .../src/renderer/families/ethereum/index.ts | 35 - .../src/renderer/families/ethereum/types.ts | 6 - .../{ethereum => evm}/AccountFooter.tsx | 2 + .../AccountHeaderManageActions.ts | 9 +- .../evm/SendAmountFields/GasPriceField.tsx | 2 + .../SendAmountFields/SelectFeeStrategy.tsx | 2 - .../{ethereum => evm}/StakeBanner.tsx | 31 +- .../StakeFlowModal/EthStakingModalBody.tsx | 18 +- .../StakeFlowModal/StakingIcon.tsx | 4 +- .../StakeFlowModal/component/ProviderItem.tsx | 9 +- .../StakeFlowModal/index.tsx | 12 +- .../{ethereum => evm}/StakeFlowModal/types.ts | 0 .../utils/getTrackProperties.ts | 0 .../StepSummaryNetworkFeesRow.tsx | 6 +- .../TransactionConfirmFields.tsx | 6 +- .../src/renderer/families/evm/index.ts | 19 +- .../families/{ethereum => evm}/modals.ts | 9 +- .../families/evm/{helpers.ts => nft.ts} | 16 +- .../{ethereum => evm}/operationDetails.tsx | 12 +- .../src/renderer/families/evm/types.ts | 1 - .../src/renderer/families/types.ts | 8 +- .../modals/Receive/steps/StepReceiveFunds.tsx | 2 +- .../renderer/modals/Send/AccountFooter.tsx | 4 + .../src/renderer/modals/Send/Body.tsx | 7 +- .../renderer/modals/Send/SendAmountFields.tsx | 1 + .../Send/fields/RecipientField.react.test.tsx | 15 +- .../renderer/modals/Send/steps/StepAmount.tsx | 3 + .../modals/Send/steps/StepConfirmation.tsx | 21 +- .../modals/Send/steps/StepSummary.tsx | 456 +-- .../modals/SignMessage/steps/StepSummary.tsx | 22 +- .../SignTransaction/steps/StepSummary.tsx | 9 +- .../Swap2/Form/FormSelectors/FormInputs.tsx | 2 + .../Swap2/Form/FormSelectors/FromRow.tsx | 36 +- .../screens/exchange/Swap2/Form/index.tsx | 12 +- .../screens/lend/modals/Enable/types.ts | 30 - .../screens/lend/modals/Supply/types.ts | 34 - .../screens/lend/modals/Withdraw/types.ts | 30 - .../tests/specs/services/liveapp-sdk.spec.ts | 56 +- .../e2e/specs/swap/dexSwap.spec.ts | 15 +- apps/ledger-live-mobile/package.json | 1 - .../src/components/EditOperationCard.tsx | 38 +- .../src/components/Nft/NftViewer.tsx | 33 +- .../src/components/OperationRow/index.tsx | 1 - .../src/components/RootDrawer/RootDrawer.tsx | 6 +- .../RootNavigator/BaseNavigator.tsx | 10 +- .../RootNavigator/types/BaseNavigator.ts | 6 +- .../RootNavigator/types/SendFundsNavigator.ts | 34 - .../types/SignTransactionNavigator.ts | 29 - .../RootNavigator/types/SwapNavigator.ts | 31 +- .../src/components/Stake/types.ts | 2 +- .../components/ValidateMessageOnDevice.tsx | 18 +- .../Web3AppWebview/liveSDKLogic.test.ts | 30 +- .../components/Web3AppWebview/liveSDKLogic.ts | 5 +- .../src/const/navigation.ts | 5 - .../families/ethereum/CurrentNetworkFee.tsx | 104 - .../families/ethereum/EditFeeUnitEthereum.tsx | 141 - .../EditEthereumSummary.tsx | 362 -- .../EditOperationPanel.tsx | 91 - .../EditTransactionNavigator.tsx | 87 - .../EditTransactionParamList.ts | 77 - .../EditTransactionFlow/MethodSelection.tsx | 281 -- .../TransactionAlreadyValidatedError.tsx | 125 - .../Ethereum1559CustomFees.tsx | 274 -- .../EthereumLegacyCustomFees.tsx | 149 - .../ethereum/EthereumCustomFees/index.tsx | 65 - .../ethereum/EthereumCustomFees/types.ts | 8 - .../ethereum/EthereumCustomFees/utils.tsx | 60 - .../ethereum/EthereumFeesStrategy.tsx | 176 - .../ethereum/EthereumNetworkFeesInfo.tsx | 61 - .../families/ethereum/ScreenEditGasLimit.tsx | 113 - .../src/families/ethereum/SendRowGasLimit.tsx | 96 - .../src/families/ethereum/SendRowsFee.tsx | 8 - .../src/families/ethereum/index.ts | 4 - .../src/families/ethereum/types.ts | 24 - .../StakingDrawer/EvmStakingDrawerBody.tsx} | 6 +- .../EvmStakingDrawerProvider.tsx} | 6 +- .../EvmStakingDrawerProviderIcon.tsx} | 2 +- .../StakingDrawer}/index.tsx | 9 +- .../StakingDrawer}/types.ts | 0 .../{ethereum => evm}/accountActions.tsx | 4 +- apps/ledger-live-mobile/src/families/index.ts | 1 - .../src/helpers/signMessageUtils.tsx | 19 - .../src/live-common-setup.ts | 2 - .../screens/Account/ListHeaderComponent.tsx | 1 - .../src/screens/OperationDetails/Content.tsx | 21 +- .../src/screens/SendFunds/01c-SelectNft.tsx | 69 +- .../screens/SendFunds/02-SelectRecipient.tsx | 2 - .../src/screens/SendFunds/03b-AmountNft.tsx | 10 - .../src/screens/SendFunds/SummaryNft.tsx | 7 +- .../src/screens/SignMessage/01-Summary.tsx | 21 +- libs/coin-evm/.eslintrc.js | 9 +- libs/coin-evm/jest.config.js | 1 + libs/coin-evm/package.json | 3 + .../fixtures/prepareTransaction.fixtures.ts | 4 +- .../integration/bridge.integration.test.ts | 12 +- .../unit/adapters/ethers.unit.test.ts | 21 + .../unit/adapters/ledger.unit.test.ts | 67 + .../unit/api/explorer/etherscan.unit.test.ts | 14 +- .../unit/api/explorer/index.unit.test.ts | 14 +- .../unit/api/gasTracker/index.unit.test.ts | 8 + .../unit/api/gasTracker/ledger.unit.test.ts | 300 +- .../__tests__/unit/api/node/rpc.unit.test.ts | 4 +- .../unit/getTransactionStatus.unit.test.ts | 792 ++-- .../__tests__/unit/hw-getAddress.unit.test.ts | 7 +- .../src/__tests__/unit/logic.unit.test.ts | 20 +- .../src/__tests__/unit/preload.unit.test.ts | 4 +- .../__tests__/unit/signOperation.unit.test.ts | 9 +- .../unit/synchronization.unit.test.ts | 2 +- .../__tests__/unit/transaction.unit.test.ts | 155 +- libs/coin-evm/src/adapters/ethers.ts | 16 +- libs/coin-evm/src/adapters/ledger.ts | 50 +- libs/coin-evm/src/api/explorer/etherscan.ts | 2 +- libs/coin-evm/src/api/explorer/ledger.ts | 6 +- libs/coin-evm/src/api/gasTracker/ledger.ts | 39 +- libs/coin-evm/src/api/node/ledger.ts | 9 +- libs/coin-evm/src/api/node/rpc.common.ts | 19 +- libs/coin-evm/src/api/node/types.ts | 4 +- libs/coin-evm/src/cli-transaction.ts | 6 +- libs/coin-evm/src/datasets/ethereum1.ts | 4 +- libs/coin-evm/src/errors.ts | 3 - libs/coin-evm/src/getTransactionStatus.ts | 49 +- libs/coin-evm/src/hw-signMessage.ts | 2 +- libs/coin-evm/src/logic.ts | 33 +- libs/coin-evm/src/prepareTransaction.ts | 4 +- libs/coin-evm/src/signOperation.ts | 2 +- libs/coin-evm/src/signer.ts | 2 +- libs/coin-evm/src/specs.ts | 223 +- libs/coin-evm/src/speculos-deviceActions.ts | 70 +- libs/coin-evm/src/synchronization.ts | 6 +- libs/coin-evm/src/types/ledger.ts | 4 +- libs/coin-evm/src/types/transaction.ts | 14 +- libs/coin-evm/tsconfig.json | 1 + .../src/account/helpers.test.ts | 4 +- libs/coin-framework/src/bot/specs.ts | 51 +- libs/coin-framework/src/bot/types.ts | 39 +- libs/coin-framework/src/mocks/helpers.ts | 2 +- libs/coin-framework/src/operation.test.ts | 8 +- libs/coin-framework/src/operation.ts | 11 +- libs/env/src/env.ts | 2 +- libs/ledger-live-common/package.json | 1 - .../src/__tests__/test-helpers/environment.ts | 2 - libs/ledger-live-common/src/apps/polyfill.ts | 7 +- libs/ledger-live-common/src/bot/cli.ts | 2 +- libs/ledger-live-common/src/bot/index.test.ts | 2 +- .../src/exchange/swap/getProviders.ts | 36 +- .../exchange/swap/hooks/useSwapTransaction.ts | 4 + .../src/exchange/swap/hooks/useToState.ts | 1 + .../swap/hooks/useUpdateMaxAmount.test.ts | 4 +- .../src/exchange/swap/utils/index.ts | 1 + .../src/exchange/swap/webApp/utils.ts | 16 +- .../bridge.integration.test.ts.snap | 60 +- .../families/bitcoin/walletApiAdapter.test.ts | 15 +- .../src/families/bitcoin/walletApiAdapter.ts | 8 +- .../src/families/celo/hw-getAddress.ts | 5 +- .../bridge.integration.test.ts.snap | 3263 ----------------- .../ethereum/__tests__/hw-signMessage.test.ts | 192 - .../__tests__/modules/erc20.unit.test.ts | 23 - .../__tests__/modules/send.unit.test.ts | 904 ----- .../__tests__/nft.integration.test.ts | 140 - .../ethereum/__tests__/nft.unit.test.ts | 252 -- .../ethereum/__tests__/signOperation.test.ts | 138 - .../synchronisation.integration.test.ts | 143 - .../__tests__/transaction.unit.test.ts | 145 - .../src/families/ethereum/api/api.test.ts | 95 - .../src/families/ethereum/api/index.ts | 382 -- .../src/families/ethereum/banner.test.ts | 29 - .../ethereum/bridge.integration.test.ts | 170 - .../src/families/ethereum/bridge/js.ts | 53 - .../src/families/ethereum/bridge/mock.ts | 150 - .../src/families/ethereum/broadcast.ts | 15 - .../src/families/ethereum/cli-transaction.ts | 127 - .../families/ethereum/createTransaction.ts | 21 - .../datasets/ethereum.scanAccounts.1.ts | 48 - .../families/ethereum/datasets/ethereum1.ts | 20 - .../families/ethereum/datasets/ethereum2.ts | 20 - .../ethereum/datasets/ethereum_classic.ts | 68 - .../ethereum/deviceTransactionConfig.ts | 33 - .../families/ethereum/estimateMaxSpendable.ts | 26 - .../src/families/ethereum/exchange.ts | 16 - .../src/families/ethereum/gas.test.ts | 68 - .../src/families/ethereum/gas.ts | 117 - .../families/ethereum/getTransactionStatus.ts | 174 - .../src/families/ethereum/hw-getAddress.ts | 17 - .../src/families/ethereum/hw-signMessage.ts | 93 - .../src/families/ethereum/logic.ts | 16 - .../src/families/ethereum/modules/erc1155.ts | 167 - .../src/families/ethereum/modules/erc20.ts | 160 - .../ethereum/modules/erc20.unit.test.ts | 49 - .../src/families/ethereum/modules/erc721.ts | 145 - .../src/families/ethereum/modules/index.ts | 182 - .../src/families/ethereum/modules/send.ts | 538 --- .../src/families/ethereum/nftResolvers.ts | 82 - .../src/families/ethereum/postSyncPatch.ts | 48 - .../families/ethereum/prepareTransaction.ts | 84 - .../src/families/ethereum/react.ts | 87 - .../src/families/ethereum/signOperation.ts | 193 - .../src/families/ethereum/specs.ts | 291 -- .../speculos-deviceActions-avalanche.ts | 53 - .../ethereum/speculos-deviceActions.ts | 81 - .../src/families/ethereum/synchronisation.ts | 713 ---- .../ethereum/synchronisation.unit.test.ts | 127 - .../src/families/ethereum/transaction.ts | 263 -- .../src/families/ethereum/types.ts | 68 - .../families/ethereum/updateTransaction.ts | 13 - .../ethereum/walletApiAdapter.test.ts | 66 - .../src/families/ethereum/walletApiAdapter.ts | 70 - .../src/families/{ethereum => evm}/banner.ts | 0 .../src/families/evm/exchange.ts | 25 + .../__tests__ => evm}/platformAdapter.test.ts | 73 +- .../{ethereum => evm}/platformAdapter.ts | 32 +- .../src/families/evm/react.react.test.ts | 4 + .../src/families/evm/walletApiAdapter.test.ts | 137 + .../src/families/evm/walletApiAdapter.ts | 79 + .../bridge.integration.test.ts.snap | 671 ++-- .../polkadot/walletApiAdapter.test.ts | 15 +- .../src/families/polkadot/walletApiAdapter.ts | 6 +- .../families/ripple/walletApiAdapter.test.ts | 15 +- .../src/families/ripple/walletApiAdapter.ts | 2 +- .../src/generated/bridge/js.ts | 2 - .../src/generated/bridge/mock.ts | 2 - .../src/generated/cli-transaction.ts | 2 - .../src/generated/deviceTransactionConfig.ts | 2 - .../src/generated/exchange.ts | 4 +- .../src/generated/hw-getAddress.ts | 2 - .../src/generated/hw-signMessage.ts | 2 - .../src/generated/platformAdapter.ts | 4 +- .../ledger-live-common/src/generated/specs.ts | 2 - .../src/generated/transaction.ts | 2 - .../ledger-live-common/src/generated/types.ts | 8 - .../src/generated/walletApiAdapter.ts | 4 +- .../src/mock/fixtures/cryptoCurrencies.ts | 4 +- libs/ledger-live-common/src/nft/support.ts | 4 +- .../src/platform/converters.test.ts | 45 +- .../src/platform/converters.ts | 13 +- .../src/platform/logic.test.ts | 103 +- libs/ledger-live-common/src/platform/logic.ts | 7 +- libs/ledger-live-common/src/platform/types.ts | 9 +- .../src/wallet-api/constants.ts | 9 +- .../src/wallet-api/converters.test.ts | 49 +- .../src/wallet-api/converters.ts | 14 +- .../src/wallet-api/helpers.ts | 2 +- .../src/wallet-api/logic.test.ts | 18 +- .../src/wallet-api/logic.ts | 14 +- .../src/wallet-api/types.ts | 11 +- .../packages/cryptoassets/src/abandonseed.ts | 2 - .../cryptoassets/src/currencies.test.ts | 4 +- .../packages/cryptoassets/src/currencies.ts | 217 +- .../packages/types-cryptoassets/src/index.ts | 9 +- .../packages/types-live/src/messages/index.ts | 5 + pnpm-lock.yaml | 29 +- 278 files changed, 3295 insertions(+), 16693 deletions(-) create mode 100644 .changeset/small-jars-hug.md delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/AccountBodyHeader.ts delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/EditStuckTransactionPanelBodyHeader.tsx delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/Body.tsx delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/index.tsx delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/steps/StepFees.tsx delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/steps/StepMethod.tsx delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/steps/StepSummaryFooter.tsx delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/types.ts delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/GasLimitField.tsx delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/GasPriceField.tsx delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/MaxFeeField.tsx delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/PriorityFeeField.tsx delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/SelectFeeStrategy.tsx delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/SendAmountFields.tsx delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/helpers.ts delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/index.ts delete mode 100644 apps/ledger-live-desktop/src/renderer/families/ethereum/types.ts rename apps/ledger-live-desktop/src/renderer/families/{ethereum => evm}/AccountFooter.tsx (99%) rename apps/ledger-live-desktop/src/renderer/families/{ethereum => evm}/AccountHeaderManageActions.ts (91%) rename apps/ledger-live-desktop/src/renderer/families/{ethereum => evm}/StakeBanner.tsx (73%) rename apps/ledger-live-desktop/src/renderer/families/{ethereum => evm}/StakeFlowModal/EthStakingModalBody.tsx (98%) rename apps/ledger-live-desktop/src/renderer/families/{ethereum => evm}/StakeFlowModal/StakingIcon.tsx (97%) rename apps/ledger-live-desktop/src/renderer/families/{ethereum => evm}/StakeFlowModal/component/ProviderItem.tsx (94%) rename apps/ledger-live-desktop/src/renderer/families/{ethereum => evm}/StakeFlowModal/index.tsx (85%) rename apps/ledger-live-desktop/src/renderer/families/{ethereum => evm}/StakeFlowModal/types.ts (100%) rename apps/ledger-live-desktop/src/renderer/families/{ethereum => evm}/StakeFlowModal/utils/getTrackProperties.ts (100%) rename apps/ledger-live-desktop/src/renderer/families/{ethereum => evm}/StepSummaryNetworkFeesRow.tsx (90%) rename apps/ledger-live-desktop/src/renderer/families/{ethereum => evm}/TransactionConfirmFields.tsx (88%) rename apps/ledger-live-desktop/src/renderer/families/{ethereum => evm}/modals.ts (55%) rename apps/ledger-live-desktop/src/renderer/families/evm/{helpers.ts => nft.ts} (62%) rename apps/ledger-live-desktop/src/renderer/families/{ethereum => evm}/operationDetails.tsx (94%) delete mode 100644 apps/ledger-live-desktop/src/renderer/screens/lend/modals/Enable/types.ts delete mode 100644 apps/ledger-live-desktop/src/renderer/screens/lend/modals/Supply/types.ts delete mode 100644 apps/ledger-live-desktop/src/renderer/screens/lend/modals/Withdraw/types.ts delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/CurrentNetworkFee.tsx delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/EditFeeUnitEthereum.tsx delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/EditTransactionFlow/EditEthereumSummary.tsx delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/EditTransactionFlow/EditOperationPanel.tsx delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/EditTransactionFlow/EditTransactionNavigator.tsx delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/EditTransactionFlow/EditTransactionParamList.ts delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/EditTransactionFlow/MethodSelection.tsx delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/EditTransactionFlow/TransactionAlreadyValidatedError.tsx delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/EthereumCustomFees/Ethereum1559CustomFees.tsx delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/EthereumCustomFees/EthereumLegacyCustomFees.tsx delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/EthereumCustomFees/index.tsx delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/EthereumCustomFees/types.ts delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/EthereumCustomFees/utils.tsx delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/EthereumFeesStrategy.tsx delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/EthereumNetworkFeesInfo.tsx delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/ScreenEditGasLimit.tsx delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/SendRowGasLimit.tsx delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/SendRowsFee.tsx delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/index.ts delete mode 100644 apps/ledger-live-mobile/src/families/ethereum/types.ts rename apps/ledger-live-mobile/src/families/{ethereum/EthereumStakingDrawer/EthereumStakingDrawerBody.tsx => evm/StakingDrawer/EvmStakingDrawerBody.tsx} (94%) rename apps/ledger-live-mobile/src/families/{ethereum/EthereumStakingDrawer/EthereumStakingDrawerProvider.tsx => evm/StakingDrawer/EvmStakingDrawerProvider.tsx} (92%) rename apps/ledger-live-mobile/src/families/{ethereum/EthereumStakingDrawer/EthereumStakingDrawerProviderIcon.tsx => evm/StakingDrawer/EvmStakingDrawerProviderIcon.tsx} (81%) rename apps/ledger-live-mobile/src/families/{ethereum/EthereumStakingDrawer => evm/StakingDrawer}/index.tsx (86%) rename apps/ledger-live-mobile/src/families/{ethereum/EthereumStakingDrawer => evm/StakingDrawer}/types.ts (100%) rename apps/ledger-live-mobile/src/families/{ethereum => evm}/accountActions.tsx (98%) delete mode 100644 apps/ledger-live-mobile/src/helpers/signMessageUtils.tsx delete mode 100644 libs/ledger-live-common/src/families/ethereum/__snapshots__/bridge.integration.test.ts.snap delete mode 100644 libs/ledger-live-common/src/families/ethereum/__tests__/hw-signMessage.test.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/__tests__/modules/erc20.unit.test.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/__tests__/modules/send.unit.test.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/__tests__/nft.integration.test.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/__tests__/nft.unit.test.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/__tests__/signOperation.test.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/__tests__/synchronisation.integration.test.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/__tests__/transaction.unit.test.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/api/api.test.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/api/index.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/banner.test.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/bridge.integration.test.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/bridge/js.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/bridge/mock.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/broadcast.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/cli-transaction.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/createTransaction.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/datasets/ethereum.scanAccounts.1.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/datasets/ethereum1.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/datasets/ethereum2.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/datasets/ethereum_classic.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/deviceTransactionConfig.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/estimateMaxSpendable.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/exchange.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/gas.test.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/gas.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/getTransactionStatus.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/hw-getAddress.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/hw-signMessage.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/logic.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/modules/erc1155.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/modules/erc20.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/modules/erc20.unit.test.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/modules/erc721.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/modules/index.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/modules/send.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/nftResolvers.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/postSyncPatch.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/prepareTransaction.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/react.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/signOperation.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/specs.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/speculos-deviceActions-avalanche.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/speculos-deviceActions.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/synchronisation.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/synchronisation.unit.test.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/transaction.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/types.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/updateTransaction.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/walletApiAdapter.test.ts delete mode 100644 libs/ledger-live-common/src/families/ethereum/walletApiAdapter.ts rename libs/ledger-live-common/src/families/{ethereum => evm}/banner.ts (100%) create mode 100644 libs/ledger-live-common/src/families/evm/exchange.ts rename libs/ledger-live-common/src/families/{ethereum/__tests__ => evm}/platformAdapter.test.ts (54%) rename libs/ledger-live-common/src/families/{ethereum => evm}/platformAdapter.ts (50%) create mode 100644 libs/ledger-live-common/src/families/evm/walletApiAdapter.test.ts create mode 100644 libs/ledger-live-common/src/families/evm/walletApiAdapter.ts diff --git a/.changeset/small-jars-hug.md b/.changeset/small-jars-hug.md new file mode 100644 index 000000000000..4e1d678a1ef2 --- /dev/null +++ b/.changeset/small-jars-hug.md @@ -0,0 +1,16 @@ +--- +"@ledgerhq/cryptoassets": major +"ledger-live-desktop": major +"live-mobile": major +"@ledgerhq/live-common": major +"@ledgerhq/types-cryptoassets": minor +"@ledgerhq/coin-framework": minor +"@ledgerhq/coin-evm": minor +"@ledgerhq/live-cli": minor +"@ledgerhq/live-env": minor +--- + +Migrate Ethereum family implementation to EVM family + +Replace the legcay Ethereum familly implementation that was present in ledger-live-common by the coin-evm lib implementation. +This change was made in order to improve scalabillity and maintainability of the evm coins, as well as more easilly integrate new evm based chains in the future. diff --git a/.github/workflows/bot-staging-explorer-eth.yml b/.github/workflows/bot-staging-explorer-eth.yml index 922222617867..7bf3a95b7cb5 100644 --- a/.github/workflows/bot-staging-explorer-eth.yml +++ b/.github/workflows/bot-staging-explorer-eth.yml @@ -1,4 +1,4 @@ -name: "[Bot] Ethereum on Staging" +name: "[Bot] Evm on Staging" on: workflow_dispatch: schedule: @@ -37,5 +37,5 @@ jobs: SLACK_API_TOKEN: ${{secrets.SLACK_API_TOKEN}} SLACK_ICON_EMOJI: ":bot-seed2:" SLACK_CHANNEL: explorer-bot-stg - BOT_FILTER_FAMILIES: ethereum + BOT_FILTER_CURRENCIES: avalanche_c_chain,bsc,polygon,ethereum,ethereum_classic,ethereum_goerli BOT_ENVIRONMENT: staging diff --git a/apps/cli/src/live-common-setup-base.ts b/apps/cli/src/live-common-setup-base.ts index d361461e4185..df6639882631 100644 --- a/apps/cli/src/live-common-setup-base.ts +++ b/apps/cli/src/live-common-setup-base.ts @@ -79,8 +79,6 @@ setSupportedCurrencies([ "persistence", "quicksilver", "internet_computer", - "ethereum_as_evm_test_only", - "polygon_as_evm_test_only", "klaytn", "polygon_zk_evm", "polygon_zk_evm_testnet", diff --git a/apps/ledger-live-desktop/package.json b/apps/ledger-live-desktop/package.json index 71fda7d3219d..bd13008a1932 100644 --- a/apps/ledger-live-desktop/package.json +++ b/apps/ledger-live-desktop/package.json @@ -51,7 +51,6 @@ "@ledgerhq/devices": "workspace:^", "@ledgerhq/domain-service": "workspace:^", "@ledgerhq/errors": "workspace:^", - "@ledgerhq/evm-tools": "workspace:^", "@ledgerhq/hw-transport": "workspace:^", "@ledgerhq/hw-transport-http": "workspace:^", "@ledgerhq/hw-transport-node-hid-singleton": "workspace:^", diff --git a/apps/ledger-live-desktop/src/config/urls.ts b/apps/ledger-live-desktop/src/config/urls.ts index 021d8d83459c..e7aeaa79e082 100644 --- a/apps/ledger-live-desktop/src/config/urls.ts +++ b/apps/ledger-live-desktop/src/config/urls.ts @@ -181,6 +181,8 @@ export const urls = { }, maxSpendable: "https://support.ledger.com/hc/en-us/articles/360012960679?utm_source=ledger_live_desktop&utm_medium=self_referral&utm_content=max_spendable_alert", + stakingEthereum: + "https://www.ledger.com/staking-ethereum?utm_source=ledger_live_desktop&utm_medium=self_referral&utm_content=ethereum", stakingTezos: "https://www.ledger.com/staking-tezos?utm_source=ledger_live_desktop&utm_medium=self_referral&utm_content=tezos", stakingTron: diff --git a/apps/ledger-live-desktop/src/live-common-set-supported-currencies.ts b/apps/ledger-live-desktop/src/live-common-set-supported-currencies.ts index ae0fb6954d8f..14aa214f808d 100644 --- a/apps/ledger-live-desktop/src/live-common-set-supported-currencies.ts +++ b/apps/ledger-live-desktop/src/live-common-set-supported-currencies.ts @@ -73,8 +73,6 @@ setSupportedCurrencies([ "velas_evm", "syscoin", "internet_computer", - "ethereum_as_evm_test_only", - "polygon_as_evm_test_only", "klaytn", "polygon_zk_evm", "polygon_zk_evm_testnet", diff --git a/apps/ledger-live-desktop/src/renderer/actions/swap.ts b/apps/ledger-live-desktop/src/renderer/actions/swap.ts index bd2751d80a0e..47b5515edf5a 100644 --- a/apps/ledger-live-desktop/src/renderer/actions/swap.ts +++ b/apps/ledger-live-desktop/src/renderer/actions/swap.ts @@ -16,9 +16,11 @@ type UPDATE_PROVIDERS_TYPE = { /* ACTIONS */ export const updateProvidersAction = createAction("SWAP/UPDATE_PROVIDERS"); + export const updateTransactionAction = createAction( "SWAP/UPDATE_TRANSACTION", ); + export const updateRateAction = createAction("SWAP/UPDATE_RATE"); export const resetSwapAction = createAction("SWAP/RESET_STATE"); @@ -27,6 +29,7 @@ export const providersSelector = createSelector( (state: State) => state.swap, swap => swap.providers, ); + export const filterAvailableToAssets = (pairs: Pair[] | null | undefined, fromId?: string) => { if (pairs === null || pairs === undefined) return null; const toAssets = []; @@ -37,6 +40,7 @@ export const filterAvailableToAssets = (pairs: Pair[] | null | undefined, fromId } return toAssets; }; + const filterAvailableFromAssets = ( pairs: Pair[] | undefined | null, allAccounts: Account[], @@ -51,6 +55,7 @@ const filterAvailableFromAssets = ( }; }) as (Account & { disabled: boolean })[]; }; + export const toSelector = createSelector( (state: State) => state.swap.pairs, pairs => @@ -60,13 +65,17 @@ export const toSelector = createSelector( return uniqueAssetList; }), ); + export const fromSelector = createSelector( - (state: State) => state.swap.pairs, - pairs => - memoize( + (state: State) => { + return state.swap.pairs; + }, + pairs => { + return memoize( (allAccounts: Array): Array => sortAccountsByStatus(filterAvailableFromAssets(pairs, allAccounts)), - ), + ); + }, ); // Put disabled accounts and subaccounts at the bottom of the list while preserving the parent/children position. @@ -104,14 +113,17 @@ export function sortAccountsByStatus(accounts: (Account & { disabled: boolean }) } return [...activeAccounts, ...disabledAccounts]; } + export const transactionSelector = createSelector( (state: State) => state.swap, swap => swap.transaction, ); + export const rateSelector = createSelector( (state: State) => state.swap, swap => swap.exchangeRate, ); + export const rateExpirationSelector = createSelector( (state: State) => state.swap, swap => swap.exchangeRateExpiration, diff --git a/apps/ledger-live-desktop/src/renderer/components/OperationsList/EditOperationPanel.tsx b/apps/ledger-live-desktop/src/renderer/components/OperationsList/EditOperationPanel.tsx index 61c7bd8122ff..92d294358d93 100644 --- a/apps/ledger-live-desktop/src/renderer/components/OperationsList/EditOperationPanel.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/OperationsList/EditOperationPanel.tsx @@ -1,9 +1,11 @@ +// FIXME: to update when implementing edit transaction on evm + import React, { useCallback, memo } from "react"; import { AccountLike, Account, Operation } from "@ledgerhq/types-live"; import { Trans } from "react-i18next"; import Alert from "~/renderer/components/Alert"; import Link from "~/renderer/components/Link"; -import { openModal, closeModal } from "~/renderer/actions/modals"; +import { closeModal } from "~/renderer/actions/modals"; import { useDispatch } from "react-redux"; import { useFeature } from "@ledgerhq/live-common/featureFlags/index"; import invariant from "invariant"; @@ -21,18 +23,21 @@ const EditOperationPanel = (props: Props) => { const handleOpenEditModal = useCallback(() => { invariant(operation.transactionRaw, "operation.transactionRaw is required"); dispatch(closeModal("MODAL_SEND")); - dispatch( - openModal("MODAL_EDIT_TRANSACTION", { - account, - parentAccount, - transactionRaw: operation.transactionRaw, - transactionHash: operation.hash, - }), - ); + // dispatch( + // openModal("MODAL_EDIT_TRANSACTION", { + // account, + // parentAccount, + // transactionRaw: operation.transactionRaw, + // transactionHash: operation.hash, + // }), + // ); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [parentAccount, account, operation, dispatch]); + if (!editEthTx?.enabled) { return null; } + return (
diff --git a/apps/ledger-live-desktop/src/renderer/components/SendFeeMode.tsx b/apps/ledger-live-desktop/src/renderer/components/SendFeeMode.tsx index 6482cc954004..45872bbb39c4 100644 --- a/apps/ledger-live-desktop/src/renderer/components/SendFeeMode.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/SendFeeMode.tsx @@ -67,4 +67,5 @@ const SendFeeMode = ({ isAdvanceMode, setAdvanceMode }: Props) => { ); }; + export default SendFeeMode; diff --git a/apps/ledger-live-desktop/src/renderer/components/SignMessageConfirm/index.tsx b/apps/ledger-live-desktop/src/renderer/components/SignMessageConfirm/index.tsx index 494db7fe6219..989b41f43237 100644 --- a/apps/ledger-live-desktop/src/renderer/components/SignMessageConfirm/index.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/SignMessageConfirm/index.tsx @@ -1,7 +1,7 @@ import styled from "styled-components"; import { useTranslation } from "react-i18next"; import React, { useEffect, useState } from "react"; -import { Account, AccountLike, AnyMessage } from "@ledgerhq/types-live"; +import { Account, AccountLike, AnyMessage, MessageProperties } from "@ledgerhq/types-live"; import { getMainAccount } from "@ledgerhq/live-common/account/index"; import { Device } from "@ledgerhq/live-common/hw/actions/types"; import { DeviceTransactionField } from "@ledgerhq/live-common/transaction/index"; @@ -12,7 +12,6 @@ import useTheme from "~/renderer/hooks/useTheme"; import Text from "~/renderer/components/Text"; import Box from "~/renderer/components/Box"; import { getLLDCoinFamily } from "~/renderer/families"; -import { MessageProperties } from "~/renderer/families/types"; const FieldText = styled(Text).attrs(() => ({ ml: 1, @@ -63,9 +62,7 @@ const SignMessageConfirm = ({ device, account, parentAccount, signMessageRequest useEffect(() => { if (signMessageRequested.standard === "EIP712") { const specific = getLLDCoinFamily(currency.family); - specific?.message - ?.getMessageProperties(mainAccount, signMessageRequested) - .then(setMessageFields); + specific?.message?.getMessageProperties(signMessageRequested).then(setMessageFields); } }, [currency, mainAccount, signMessageRequested]); diff --git a/apps/ledger-live-desktop/src/renderer/components/Stepper/index.tsx b/apps/ledger-live-desktop/src/renderer/components/Stepper/index.tsx index 6bea1f1de208..61f7a613eb9e 100644 --- a/apps/ledger-live-desktop/src/renderer/components/Stepper/index.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/Stepper/index.tsx @@ -13,8 +13,8 @@ export type Step = { id: T; label?: React.ReactNode; excludeFromBreadcrumb?: boolean; - component: React.ComponentType; - footer?: React.ComponentType; + component: React.FC | React.ComponentType; + footer?: React.FC | React.ComponentType; onBack?: ((a: StepProps) => void) | null; backButtonComponent?: React.ReactNode; noScroll?: boolean; diff --git a/apps/ledger-live-desktop/src/renderer/components/TransactionConfirm/index.tsx b/apps/ledger-live-desktop/src/renderer/components/TransactionConfirm/index.tsx index ce350278ee5e..29038b7c45e1 100644 --- a/apps/ledger-live-desktop/src/renderer/components/TransactionConfirm/index.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/TransactionConfirm/index.tsx @@ -54,6 +54,7 @@ const FeesField = ({ account, parentAccount, status, field }: FieldComponentProp { : undefined; const editEthTx = useFeature("editEthTx"); const editable = editEthTx?.enabled && isEditableOperation(mainAccount, operation); - const dispatch = useDispatch(); - const handleOpenEditModal = useCallback( - (account, parentAccount, transactionRaw, transactionHash) => { - setDrawer(undefined); - if (subOperations.length > 0 && isToken) { - // if the operation is a token operation,(ERC-20 send), in order to speedup/cancel we need to find the subAccount - const opAccount = findSubAccountById(account, subOperations[0].accountId); - dispatch( - openModal("MODAL_EDIT_TRANSACTION", { - account: opAccount, - parentAccount: account, - transactionRaw, - transactionHash, - }), - ); - } else { - dispatch( - openModal("MODAL_EDIT_TRANSACTION", { - account, - parentAccount, - transactionRaw, - transactionHash, - }), - ); - } - }, - [dispatch, isToken, subOperations], - ); + // const dispatch = useDispatch(); + // const handleOpenEditModal = useCallback( + // (account, parentAccount, transactionRaw, transactionHash) => { + // setDrawer(undefined); + // if (subOperations.length > 0 && isToken) { + // // if the operation is a token operation,(ERC-20 send), in order to speedup/cancel we need to find the subAccount + // const opAccount = findSubAccountById(account, subOperations[0].accountId); + // dispatch( + // openModal("MODAL_EDIT_TRANSACTION", { + // account: opAccount, + // parentAccount: account, + // transactionRaw, + // transactionHash, + // }), + // ); + // } else { + // dispatch( + // openModal("MODAL_EDIT_TRANSACTION", { + // account, + // parentAccount, + // transactionRaw, + // transactionHash, + // }), + // ); + // } + // }, + // [dispatch, isToken, subOperations], + // ); + + const handleOpenEditModal = ( + _account: unknown, + _parentAccount: unknown, + _transactionRaw: unknown, + _transactionHash: unknown, + ) => {}; + // pending transactions that exceeds 5 minutes are considered as stuck transactions const isStuck = new Date().getTime() - operation.date.getTime() > getEnv("ETHEREUM_STUCK_TRANSACTION_TIMEOUT"); diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/AccountBodyHeader.ts b/apps/ledger-live-desktop/src/renderer/families/ethereum/AccountBodyHeader.ts deleted file mode 100644 index c07baa811e3e..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/AccountBodyHeader.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./EditStuckTransactionPanelBodyHeader"; diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/EditStuckTransactionPanelBodyHeader.tsx b/apps/ledger-live-desktop/src/renderer/families/ethereum/EditStuckTransactionPanelBodyHeader.tsx deleted file mode 100644 index d4714f9978f6..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/EditStuckTransactionPanelBodyHeader.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React, { memo } from "react"; -import Box from "~/renderer/components/Box"; -import { useFeature } from "@ledgerhq/live-common/featureFlags/index"; -import { Account, AccountLike } from "@ledgerhq/types-live"; -import EditOperationPanel from "~/renderer/components/OperationsList/EditOperationPanel"; -import { getStuckAccountAndOperation } from "@ledgerhq/live-common/operation"; -import invariant from "invariant"; - -type Props = { - account: AccountLike; - parentAccount: Account | undefined | null; -}; - -const EditStuckTransactionPanelBodyHeader = (props: Props) => { - const { account, parentAccount } = props; - const editEthTx = useFeature("editEthTx"); - if (!editEthTx?.enabled) { - return null; - } - invariant(account, "account required"); - // for ethereum family, check if there is a stuck transaction. If so, display a warning panel with "speed up or cancel" button - const stuckAccountAndOperation = getStuckAccountAndOperation(account, parentAccount); - return ( - - {stuckAccountAndOperation ? ( - - ) : null} - - ); -}; - -export default memo(EditStuckTransactionPanelBodyHeader); diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/Body.tsx b/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/Body.tsx deleted file mode 100644 index 69a51c7bf97c..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/Body.tsx +++ /dev/null @@ -1,272 +0,0 @@ -import React, { useCallback, useState } from "react"; -import { BigNumber } from "bignumber.js"; -import { connect } from "react-redux"; -import { compose } from "redux"; -import { createStructuredSelector } from "reselect"; -import { Trans, withTranslation } from "react-i18next"; -import { TFunction } from "i18next"; -import { UserRefusedOnDevice } from "@ledgerhq/errors"; -import { addPendingOperation, getMainAccount } from "@ledgerhq/live-common/account/index"; -import { getAccountCurrency } from "@ledgerhq/live-common/account/helpers"; -import useBridgeTransaction from "@ledgerhq/live-common/bridge/useBridgeTransaction"; -import { Account, AccountLike, Operation } from "@ledgerhq/types-live"; -import { Transaction } from "@ledgerhq/live-common/generated/types"; -import { EIP1559ShouldBeUsed } from "@ledgerhq/live-common/families/ethereum/transaction"; -import { TransactionRaw as EthereumTransactionRaw } from "@ledgerhq/live-common/families/ethereum/types"; -import { isEditableOperation } from "@ledgerhq/live-common/operation"; -import logger from "~/renderer/logger"; -import Stepper from "~/renderer/components/Stepper"; -import { SyncSkipUnderPriority } from "@ledgerhq/live-common/bridge/react/index"; -import { closeModal, openModal } from "~/renderer/actions/modals"; -import { accountsSelector } from "~/renderer/reducers/accounts"; -import { updateAccountWithUpdater } from "~/renderer/actions/accounts"; -import { getCurrentDevice } from "~/renderer/reducers/devices"; -import { Device } from "@ledgerhq/live-common/hw/actions/types"; -import StepMethod, { StepMethodFooter } from "./steps/StepMethod"; -import StepFees, { StepFeesFooter } from "./steps/StepFees"; -import StepConnectDevice from "~/renderer/modals/Send/steps/StepConnectDevice"; -import StepSummary from "~/renderer/modals/Send/steps/StepSummary"; -import { StepSummaryFooter } from "./steps/StepSummaryFooter"; -import { fromTransactionRaw } from "@ledgerhq/live-common/transaction/index"; -import StepConfirmation, { - StepConfirmationFooter, -} from "~/renderer/modals/Send/steps/StepConfirmation"; -import { St, StepId } from "./types"; -import invariant from "invariant"; -import { getEnv } from "@ledgerhq/live-env"; - -export type Data = { - account: AccountLike | undefined | null; - parentAccount: Account | undefined | null; - recipient?: string; - amount?: BigNumber; - transaction?: Transaction; - transactionRaw: EthereumTransactionRaw; - transactionHash: string; -}; - -type OwnProps = { - stepId: StepId; - onChangeStepId: (a: StepId) => void; - onClose?: () => void | undefined; - params: Data; -}; - -type StateProps = { - t: TFunction; - device: Device | undefined | null; - accounts: Account[]; - closeModal: (a: string) => void; - openModal: (b: string, a: unknown) => void; - updateAccountWithUpdater: (b: string, a: (a: Account) => Account) => void; -}; -type Props = {} & OwnProps & StateProps; - -const createSteps = (): St[] => [ - { - id: "method", - label: , - component: StepMethod, - footer: StepMethodFooter, - }, - { - id: "fees", - label: , - component: StepFees, - footer: StepFeesFooter, - onBack: ({ transitionTo }) => transitionTo("method"), - }, - { - id: "summary", - label: , - component: StepSummary, - footer: StepSummaryFooter, - onBack: ({ transitionTo, editType }) => { - // transit to "fees" page for speedup flow and skip the "fees" page for cancel flow - transitionTo(editType === "speedup" ? "fees" : "method"); - }, - }, - { - id: "device", - label: , - component: StepConnectDevice, - onBack: ({ transitionTo }) => transitionTo("summary"), - }, - { - id: "confirmation", - label: , - excludeFromBreadcrumb: true, - component: StepConfirmation, - footer: StepConfirmationFooter, - onBack: null, - }, -]; - -const mapStateToProps = createStructuredSelector({ - device: getCurrentDevice, - accounts: accountsSelector, -}); - -const mapDispatchToProps = { - closeModal, - openModal, - updateAccountWithUpdater, -}; - -const Body = ({ - t, - device, - openModal, - closeModal, - onChangeStepId, - onClose, - stepId, - params, - accounts, - updateAccountWithUpdater, -}: Props) => { - const [steps] = useState(() => createSteps()); - const { - transaction, - setTransaction, - updateTransaction, - account, - parentAccount, - status, - bridgeError, - bridgePending, - } = useBridgeTransaction(() => { - const parentAccount = params && params.parentAccount; - const account = (params && params.account) || accounts[0]; - return { - account, - parentAccount, - transaction: fromTransactionRaw(params.transactionRaw as EthereumTransactionRaw), - }; - }); - invariant(account, "account required"); - const [optimisticOperation, setOptimisticOperation] = useState(null); - const [transactionError, setTransactionError] = useState(null); - const [signed, setSigned] = useState(false); - - const currencyName = getAccountCurrency(account).name; - - const handleCloseModal = useCallback(() => { - closeModal("MODAL_EDIT_TRANSACTION"); - }, [closeModal]); - - const handleRetry = useCallback(() => { - setTransactionError(null); - setOptimisticOperation(null); - setSigned(false); - }, []); - - const handleTransactionError = useCallback((error: Error) => { - if (!(error instanceof UserRefusedOnDevice)) { - logger.critical(error); - } - setTransactionError(error); - }, []); - - const handleOperationBroadcasted = useCallback( - (optimisticOperation: Operation) => { - const mainAccount = getMainAccount(account, parentAccount); - updateAccountWithUpdater(mainAccount.id, account => - addPendingOperation(account, optimisticOperation), - ); - setOptimisticOperation(optimisticOperation); - setTransactionError(null); - }, - [account, parentAccount, updateAccountWithUpdater], - ); - - const handleStepChange = useCallback(e => onChangeStepId(e.id), [onChangeStepId]); - const error = transactionError || bridgeError; - const mainAccount = getMainAccount(account, parentAccount); - const feePerGas = new BigNumber( - EIP1559ShouldBeUsed(mainAccount.currency) - ? (params.transactionRaw as EthereumTransactionRaw).maxFeePerGas ?? "0" - : (params.transactionRaw as EthereumTransactionRaw).gasPrice ?? "0", - ); - const feeValue = new BigNumber( - (params.transactionRaw as EthereumTransactionRaw).userGasLimit || - (params.transactionRaw as EthereumTransactionRaw).estimatedGasLimit || - 0, - ).times(feePerGas); - const haveFundToCancel = mainAccount.balance.gt( - feeValue.times(1 + getEnv("EDIT_TX_EIP1559_MAXFEE_GAP_CANCEL_FACTOR")), - ); - const haveFundToSpeedup = mainAccount.balance.gt( - feeValue - .times(1 + getEnv("EDIT_TX_EIP1559_FEE_GAP_SPEEDUP_FACTOR")) - .plus(account.type === "Account" ? new BigNumber(params.transactionRaw.amount) : 0), - ); - // log account and fees info - logger.log(`main account address: ${mainAccount.freshAddress}`); - logger.log(`main account balance: ${mainAccount.balance.toFixed()}`); - logger.log(`feeValue: ${feeValue.toFixed()}`); - logger.log(`pending transaction amount: ${params.transactionRaw.amount}`); - let isOldestEditableOperation = true; - mainAccount.pendingOperations.forEach((operation: Operation) => { - if (isEditableOperation(account, operation)) { - if ( - operation.transactionSequenceNumber !== undefined && - params.transactionRaw.nonce !== undefined && - operation.transactionSequenceNumber < params.transactionRaw.nonce - ) { - isOldestEditableOperation = false; - } - } - }); - - const [editType, setEditType] = useState( - isOldestEditableOperation && haveFundToSpeedup ? "speedup" : haveFundToCancel ? "cancel" : "", - ); - const handleSetEditType = useCallback(editType => setEditType(editType), []); - - const stepperProps = { - title: t("operation.edit.title"), - stepId, - steps, - device, - account, - parentAccount, - transaction, - signed, - currencyName, - hideBreadcrumb: false, - error, - status, - bridgePending, - optimisticOperation, - openModal, - onClose, - setSigned, - closeModal: handleCloseModal, - onChangeTransaction: setTransaction, - onRetry: handleRetry, - onStepChange: handleStepChange, - onOperationBroadcasted: handleOperationBroadcasted, - onTransactionError: handleTransactionError, - updateTransaction, - setEditType: handleSetEditType, - editType, - transactionRaw: params.transactionRaw, - transactionHash: params.transactionHash, - haveFundToSpeedup, - haveFundToCancel, - isOldestEditableOperation, - }; - - return ( - - {stepId === "confirmation" ? null : } - - ); -}; - -const m = compose( - connect(mapStateToProps, mapDispatchToProps), - withTranslation(), -)(Body) as React.ComponentType; -export default m; diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/index.tsx b/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/index.tsx deleted file mode 100644 index f5f2829660ef..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/index.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React, { useCallback, useState, memo } from "react"; -import { DomainServiceProvider } from "@ledgerhq/domain-service/hooks/index"; -import Modal from "~/renderer/components/Modal"; -import Body from "./Body"; -import { Account, AccountLike, TransactionCommonRaw } from "@ledgerhq/types-live"; -import { TransactionRaw as EthereumTransactionRaw } from "@ledgerhq/live-common/families/ethereum/types"; -import { StepId } from "./types"; - -export type Props = { - account: AccountLike | undefined | null; - parentAccount: Account | undefined | null; - transactionRaw: TransactionCommonRaw; - transactionHash: string; -}; - -const EditTransactionModal = ({ - account, - parentAccount, - transactionRaw, - transactionHash, -}: Props) => { - const [stepId, setStepId] = useState("method"); - const handleReset = useCallback(() => setStepId("method"), []); - const handleStepChange = useCallback(stepId => setStepId(stepId), []); - return ( - - ( - - )} - /> - - ); -}; - -export default memo(EditTransactionModal); diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/steps/StepFees.tsx b/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/steps/StepFees.tsx deleted file mode 100644 index 9e2aac18eb81..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/steps/StepFees.tsx +++ /dev/null @@ -1,174 +0,0 @@ -import React, { Fragment, PureComponent, memo } from "react"; -import { Trans } from "react-i18next"; -import { getMainAccount } from "@ledgerhq/live-common/account/index"; -import Box from "~/renderer/components/Box"; -import Button from "~/renderer/components/Button"; -import CurrencyDownStatusAlert from "~/renderer/components/CurrencyDownStatusAlert"; -import ErrorBanner from "~/renderer/components/ErrorBanner"; -import Alert from "~/renderer/components/Alert"; -import TranslatedError from "~/renderer/components/TranslatedError"; -import SendAmountFields from "../../../../modals/Send/SendAmountFields"; -import { StepProps } from "../types"; -import { BigNumber } from "bignumber.js"; -import { EIP1559ShouldBeUsed } from "@ledgerhq/live-common/families/ethereum/transaction"; -import { TransactionRaw as EthereumTransactionRaw } from "@ledgerhq/live-common/families/ethereum/types"; -import { TransactionHasBeenValidatedError, NotEnoughGas } from "@ledgerhq/errors"; -import { apiForCurrency } from "@ledgerhq/live-common/families/ethereum/api/index"; -import logger from "~/renderer/logger"; -import { NotOwnedNft as Erc721NotOwnedNft } from "@ledgerhq/live-common/families/ethereum/modules/erc721"; -import { - NotOwnedNft as Erc1155NotOwnedNft, - NotEnoughNftOwned as Erc1155NotEnoughNftOwned, -} from "@ledgerhq/live-common/families/ethereum/modules/erc1155"; - -const StepFees = (props: StepProps) => { - const { - t, - account, - parentAccount, - transaction, - onChangeTransaction, - status, - bridgePending, - updateTransaction, - } = props; - const mainAccount = account ? getMainAccount(account, parentAccount) : null; - if (!mainAccount) return null; - const transactionRaw = props.transactionRaw as EthereumTransactionRaw; - const feePerGas = new BigNumber( - EIP1559ShouldBeUsed(mainAccount.currency) - ? transactionRaw?.maxFeePerGas ?? 0 - : transactionRaw?.gasPrice ?? 0, - ); - const feeValue = new BigNumber( - transactionRaw.userGasLimit || transactionRaw.estimatedGasLimit || 0, - ) - .times(feePerGas) - .div(new BigNumber(10).pow(mainAccount.unit.magnitude)); - // log fees info - logger.log(`transactionRaw.maxFeePerGas: ${transactionRaw.maxFeePerGas}`); - logger.log(`transactionRaw.gasPrice: ${transactionRaw.gasPrice}`); - logger.log(`transactionRaw.maxPriorityFeePerGas: ${transactionRaw.maxPriorityFeePerGas}`); - - let maxPriorityFeePerGasinGwei, maxFeePerGasinGwei, maxGasPriceinGwei; - if (EIP1559ShouldBeUsed(mainAccount.currency)) { - // dividedBy 1000000000 to convert from wei to gwei - maxPriorityFeePerGasinGwei = new BigNumber(transactionRaw?.maxPriorityFeePerGas ?? 0) - .dividedBy(1000000000) - .toFixed(); - maxFeePerGasinGwei = new BigNumber(transactionRaw?.maxFeePerGas ?? 0) - .dividedBy(1000000000) - .toFixed(); - } else { - maxGasPriceinGwei = new BigNumber(transactionRaw?.gasPrice ?? 0) - .dividedBy(1000000000) - .toFixed(); - } - return ( - - {mainAccount ? : null} - {account && transaction && mainAccount && ( - - - - )} - - {EIP1559ShouldBeUsed(mainAccount.currency) ? ( // Display the fees info of the pending transaction (network fee, maxPriorityFeePerGas, maxFeePerGas, maxGasPrice) -
    - {t("operation.edit.previousFeesInfo.pendingTransactionFeesInfo")} -
  • {`${t("operation.edit.previousFeesInfo.networkfee")} ${feeValue} ${ - mainAccount.currency.ticker - }`}
  • -
  • {`${t( - "operation.edit.previousFeesInfo.maxPriorityFee", - )} ${maxPriorityFeePerGasinGwei} Gwei`}
  • -
  • {`${t("operation.edit.previousFeesInfo.maxFee")} ${maxFeePerGasinGwei} Gwei`}
  • -
- ) : ( -
    - {t("operation.edit.previousFeesInfo.pendingTransactionFeesInfo")} -
  • {`${t("operation.edit.previousFeesInfo.networkfee")} ${feeValue} ${ - mainAccount.currency.ticker - }`}
  • -
  • {`${t("operation.edit.previousFeesInfo.gasPrice")} ${maxGasPriceinGwei} Gwei`}
  • -
- )} -
-
- ); -}; - -export class StepFeesFooter extends PureComponent { - state = { - transactionHasBeenValidated: false, - }; - - onNext = async () => { - const { transitionTo } = this.props; - transitionTo("summary"); - }; - - componentDidMount() { - const { account, parentAccount, transaction, transactionHash } = this.props; - if (!account || !transaction || !transactionHash) return; - const mainAccount = getMainAccount(account, parentAccount); - if (mainAccount.currency.family !== "ethereum") return; - apiForCurrency(mainAccount.currency) - .getTransactionByHash(transactionHash) - .then(tx => { - if (tx?.confirmations) { - this.setState({ transactionHasBeenValidated: true }); - } - }); - } - - render() { - const { bridgePending, status } = this.props; - const { errors } = status; - // exclude "NotOwnedNft" and "NotEnoughNftOwned" error if it's a nft speedup operation - let errorCount = Object.keys(errors).length; - if ( - errors.amount && - ((errors.amount as Error) instanceof Erc721NotOwnedNft || - (errors.amount as Error) instanceof Erc1155NotOwnedNft || - (errors.amount as Error) instanceof Erc1155NotEnoughNftOwned) - ) { - errorCount = errorCount - 1; - } - return ( - <> - {this.state.transactionHasBeenValidated ? ( - - ) : errors.gasPrice && errors.gasPrice instanceof NotEnoughGas ? ( - - } /> - - ) : errorCount ? ( - - } /> - - ) : null} - - - ); - } -} - -export default memo(StepFees); diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/steps/StepMethod.tsx b/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/steps/StepMethod.tsx deleted file mode 100644 index 10c997d8ecb2..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/steps/StepMethod.tsx +++ /dev/null @@ -1,237 +0,0 @@ -import React, { PureComponent, memo, useCallback } from "react"; -import { Trans } from "react-i18next"; -import Box from "~/renderer/components/Box"; -import Button from "~/renderer/components/Button"; -import styled from "styled-components"; -import CheckBox from "~/renderer/components/CheckBox"; -import Text from "~/renderer/components/Text"; -import { StepProps } from "../types"; -import { BigNumber } from "bignumber.js"; -import { getAccountBridge } from "@ledgerhq/live-common/bridge/index"; -import { Flex } from "@ledgerhq/react-ui"; -import { openURL } from "~/renderer/linking"; -import { urls } from "~/config/urls"; -import { getEnv } from "@ledgerhq/live-env"; -import { getMainAccount } from "@ledgerhq/live-common/account/index"; -import ErrorBanner from "~/renderer/components/ErrorBanner"; -import { TransactionHasBeenValidatedError } from "@ledgerhq/errors"; -import { apiForCurrency } from "@ledgerhq/live-common/families/ethereum/api/index"; -import invariant from "invariant"; - -const EditTypeWrapper = styled(Box)<{ selected: boolean }>` - border: ${p => - `1px solid ${ - p.selected ? p.theme.colors.palette.primary.main : p.theme.colors.palette.divider - }`}; - padding: 20px 16px; - border-radius: 4px; - &:hover { - cursor: "pointer"; - } -`; -const EditTypeHeader = styled(Box)<{ selected: boolean }>` - color: ${p => - p.selected ? p.theme.colors.palette.primary.main : p.theme.colors.palette.text.shade50}; - margin-left: 10px; -`; - -const Description = styled(Box)<{ selected: boolean }>` - color: ${p => (p.selected ? "white" : "gray")}; - margin-top: 5px; - margin-left: 15px; - width: 400px; -`; - -const StepMethod = ({ - account, - editType, - setEditType, - t, - haveFundToSpeedup, - haveFundToCancel, - isOldestEditableOperation, -}: StepProps) => { - invariant(account, "account required"); - const isCancel = editType === "cancel"; - const isSpeedup = editType === "speedup"; - const disableSpeedup = !haveFundToSpeedup || !isOldestEditableOperation; - const disableCancel = !haveFundToCancel; - const handleSpeedupClick = useCallback(() => { - if (!disableSpeedup) { - setEditType("speedup"); - } - }, [disableSpeedup, setEditType]); - const handleCancelClick = useCallback(() => { - if (!disableCancel) { - setEditType("cancel"); - } - }, [disableCancel, setEditType]); - const handleLearnMoreClick = useCallback(() => { - openURL(urls.editEthTx.learnMore); - }, []); - return ( - - - - - - - - - - - - - {haveFundToSpeedup && isOldestEditableOperation ? ( - - ) : isOldestEditableOperation ? ( - - ) : ( - - )} - - - - - - - - - - - - - - - - - {haveFundToCancel ? ( - - ) : ( - - )} - - - - - - - {t("operation.edit.learnMore")} - - - ); -}; - -export class StepMethodFooter extends PureComponent { - state = { - transactionHasBeenValidated: false, - }; - - handleContinueClick = () => { - const { transitionTo, editType, updateTransaction, account, parentAccount, transactionRaw } = - this.props; - invariant(account && transactionRaw, "account and transactionRaw required"); - const bridge = getAccountBridge(account, parentAccount); - if (editType === "speedup") { - updateTransaction(tx => - bridge.updateTransaction(tx, { - amount: new BigNumber(transactionRaw.amount), - data: transactionRaw.data ? Buffer.from(transactionRaw.data, "hex") : undefined, - nonce: transactionRaw.nonce, - recipient: transactionRaw.recipient, - mode: transactionRaw.mode, - networkInfo: null, // force to update network fee - feesStrategy: "fast", // set "fast" as default option for speedup flow - maxFeePerGas: null, - maxPriorityFeePerGas: null, - gasPrice: null, - useAllAmount: false, - }), - ); - } else { - const mainAccount = getMainAccount(account, parentAccount); - updateTransaction(tx => - bridge.updateTransaction(tx, { - amount: new BigNumber(0), - allowZeroAmount: true, - data: undefined, - nonce: transactionRaw.nonce, - mode: "send", - recipient: mainAccount.freshAddress, - // increase gas fees in case of cancel flow as we don't have the fees input screen for cancel flow - maxFeePerGas: transactionRaw.maxFeePerGas - ? new BigNumber(transactionRaw.maxFeePerGas) - .times(1 + getEnv("EDIT_TX_EIP1559_MAXFEE_GAP_CANCEL_FACTOR")) - .integerValue() - : null, - maxPriorityFeePerGas: transactionRaw.maxPriorityFeePerGas - ? new BigNumber(transactionRaw.maxPriorityFeePerGas) - .times(1 + getEnv("EDIT_TX_EIP1559_FEE_GAP_SPEEDUP_FACTOR")) - .integerValue() - : null, - gasPrice: transactionRaw.gasPrice - ? new BigNumber(transactionRaw.gasPrice) - .times(1 + getEnv("EDIT_TX_LEGACY_GASPRICE_GAP_CANCEL_FACTOR")) - .integerValue() - : null, - feesStrategy: "custom", - useAllAmount: false, - }), - ); - } - // skip fees input screen for cancel flow - transitionTo(editType === "speedup" ? "fees" : "summary"); - }; - - componentDidMount() { - const { account, parentAccount, transaction, transactionHash } = this.props; - if (!account || !transaction || !transactionHash) return; - const mainAccount = getMainAccount(account, parentAccount); - if (mainAccount.currency.family !== "ethereum") return; - apiForCurrency(mainAccount.currency) - .getTransactionByHash(transactionHash) - .then(tx => { - if (tx?.confirmations) { - this.setState({ transactionHasBeenValidated: true }); - } - }); - } - - render() { - const { t, haveFundToSpeedup, haveFundToCancel, isOldestEditableOperation } = this.props; - - return ( - <> - {this.state.transactionHasBeenValidated ? ( - - ) : null} - - - ); - } -} - -export default memo(StepMethod); diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/steps/StepSummaryFooter.tsx b/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/steps/StepSummaryFooter.tsx deleted file mode 100644 index 44467b6b8d48..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/steps/StepSummaryFooter.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import React, { PureComponent } from "react"; -import { Trans } from "react-i18next"; -import { getMainAccount } from "@ledgerhq/live-common/account/index"; -import Button from "~/renderer/components/Button"; -import { StepProps } from "../types"; -import { apiForCurrency } from "@ledgerhq/live-common/families/ethereum/api/index"; -import ErrorBanner from "~/renderer/components/ErrorBanner"; -import { TransactionHasBeenValidatedError } from "@ledgerhq/errors"; -import { NotOwnedNft as Erc721NotOwnedNft } from "@ledgerhq/live-common/families/ethereum/modules/erc721"; -import { - NotOwnedNft as Erc1155NotOwnedNft, - NotEnoughNftOwned as Erc1155NotEnoughNftOwned, -} from "@ledgerhq/live-common/families/ethereum/modules/erc1155"; - -export class StepSummaryFooter extends PureComponent { - state = { - transactionHasBeenValidated: false, - }; - - onNext = async () => { - const { transitionTo } = this.props; - transitionTo("device"); - }; - - componentDidMount() { - const { account, parentAccount, transactionHash } = this.props; - if (!account || !transactionHash) return; - const mainAccount = getMainAccount(account, parentAccount); - if (mainAccount.currency.family !== "ethereum") return; - apiForCurrency(mainAccount.currency) - .getTransactionByHash(transactionHash) - .then(tx => { - if (tx?.confirmations) { - this.setState({ transactionHasBeenValidated: true }); - } - }); - } - - render() { - const { account, status, bridgePending } = this.props; - if (!account) return null; - const { errors } = status; - // exclude "NotOwnedNft" and "NotEnoughNftOwned" error if it's a nft speedup operation - let errorCount = Object.keys(errors).length; - if ( - errors.amount && - ((errors.amount as Error) instanceof Erc721NotOwnedNft || - (errors.amount as Error) instanceof Erc1155NotOwnedNft || - (errors.amount as Error) instanceof Erc1155NotEnoughNftOwned) - ) { - errorCount = errorCount - 1; - } - const canNext = !bridgePending && !errorCount && !this.state.transactionHasBeenValidated; - return ( - <> - {this.state.transactionHasBeenValidated ? ( - - ) : null} - - - ); - } -} diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/types.ts b/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/types.ts deleted file mode 100644 index 7b32b1a10a25..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/EditTransactionModal/types.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { TFunction } from "i18next"; -import { Account, AccountLike, Operation } from "@ledgerhq/types-live"; -import { Transaction, TransactionStatus } from "@ledgerhq/live-common/generated/types"; -import { Device } from "@ledgerhq/live-common/hw/actions/types"; -import { Step } from "~/renderer/components/Stepper"; -import { TransactionRaw as EthereumTransactionRaw } from "@ledgerhq/live-common/families/ethereum/types"; - -export type StepId = "method" | "fees" | "summary" | "device" | "confirmation"; - -export type StepProps = { - t: TFunction; - transitionTo: (a: string) => void; - device: Device | undefined | null; - account: AccountLike | undefined | null; - parentAccount: Account | undefined | null; - transaction: Transaction | undefined | null; - status: TransactionStatus; - bridgePending: boolean; - error: Error | undefined | null; - optimisticOperation: Operation | undefined | null; - closeModal: (a: void) => void; - openModal: (b: string, a: unknown) => void; - onChangeTransaction: (a: Transaction) => void; - onTransactionError: (a: Error) => void; - onOperationBroadcasted: (a: Operation) => void; - onRetry: (a: void) => void; - setSigned: (a: boolean) => void; - signed: boolean; - updateTransaction: (updater: (a: Transaction) => void) => void; - currencyName: string | undefined | null; - transactionRaw: EthereumTransactionRaw; - transactionHash: string; - editType: string | undefined | null; - haveFundToSpeedup: boolean; - haveFundToCancel: boolean; - isOldestEditableOperation: boolean; - setEditType: (a: string) => void; -}; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type St = Step; diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/GasLimitField.tsx b/apps/ledger-live-desktop/src/renderer/families/ethereum/GasLimitField.tsx deleted file mode 100644 index 28ad39feba9c..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/GasLimitField.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { useCallback, useState } from "react"; -import { - Transaction as EthereumTransaction, - TransactionStatus, -} from "@ledgerhq/live-common/families/ethereum/types"; -import { getGasLimit } from "@ledgerhq/live-common/families/ethereum/transaction"; -import { Result } from "@ledgerhq/live-common/lib/bridge/useBridgeTransaction"; -import { getAccountBridge } from "@ledgerhq/live-common/bridge/index"; -import { getMainAccount } from "@ledgerhq/live-common/account/index"; -import { Account, AccountLike } from "@ledgerhq/types-live"; -import { useTranslation } from "react-i18next"; -import { Button } from "@ledgerhq/react-ui"; -import { BigNumber } from "bignumber.js"; -import invariant from "invariant"; -import Box from "~/renderer/components/Box"; -import Input from "~/renderer/components/Input"; -import Label from "~/renderer/components/Label"; - -type Props = { - transaction: EthereumTransaction; - account: AccountLike; - parentAccount: Account | null | undefined; - status: TransactionStatus; - updateTransaction: Result["updateTransaction"]; -}; - -const DEFAULT_GAS_LIMIT = new BigNumber(21000); - -const AdvancedOptions = ({ - account, - parentAccount, - transaction, - status, - updateTransaction, -}: Props) => { - invariant(transaction.family === "ethereum", "AdvancedOptions: ethereum family expected"); - const mainAccount = getMainAccount(account, parentAccount); - invariant(mainAccount, "Account required"); - const [editable, setEditable] = useState(false); - const { t } = useTranslation(); - - const onGasLimitChange = useCallback( - (str: string) => { - const bridge = getAccountBridge(mainAccount); - let userGasLimit = new BigNumber(str || 0); - if (!userGasLimit.isFinite()) { - userGasLimit = DEFAULT_GAS_LIMIT; - } - updateTransaction(transaction => - bridge.updateTransaction(transaction, { userGasLimit, feesStrategy: "custom" }), - ); - }, - [mainAccount, updateTransaction], - ); - - const onEditClick = useCallback(() => setEditable(true), [setEditable]); - - const gasLimit = getGasLimit(transaction); - const { gasLimit: gasLimitError } = status.errors; - const { gasLimit: gasLimitWarning } = status.warnings; - - return ( - - - - - {editable ? ( - - - - ) : ( - - - - - )} - - ); -}; - -export default AdvancedOptions; diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/GasPriceField.tsx b/apps/ledger-live-desktop/src/renderer/families/ethereum/GasPriceField.tsx deleted file mode 100644 index 17047dc32532..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/GasPriceField.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { useCallback } from "react"; -import { BigNumber } from "bignumber.js"; -import invariant from "invariant"; -import { Account, TransactionCommonRaw } from "@ledgerhq/types-live"; -import { - Transaction, - TransactionStatus, - TransactionRaw as EthereumTransactionRaw, -} from "@ledgerhq/live-common/families/ethereum/types"; -import { getAccountBridge } from "@ledgerhq/live-common/bridge/index"; -import FeeSliderField from "~/renderer/components/FeeSliderField"; -import { getEnv } from "@ledgerhq/live-env"; -import { Range, inferDynamicRange } from "@ledgerhq/live-common/range"; - -type Props = { - account: Account; - transaction: Transaction; - status: TransactionStatus; - updateTransaction: (updater: (_: Transaction) => Transaction) => void; - transactionRaw?: TransactionCommonRaw; -}; -const fallbackGasPrice = inferDynamicRange(BigNumber(10e9)); -let lastNetworkGasPrice: Range | undefined; // local cache of last value to prevent extra blinks - -const FeesField = ({ account, transaction, status, updateTransaction, transactionRaw }: Props) => { - invariant(transaction.family === "ethereum", "FeeField: ethereum family expected"); - const bridge = getAccountBridge(account); - const onGasPriceChange = useCallback( - gasPrice => { - updateTransaction(transaction => - bridge.updateTransaction(transaction, { - gasPrice, - feesStrategy: "advanced", - }), - ); - }, - [updateTransaction, bridge], - ); - const networkGasPrice = transaction.networkInfo && transaction.networkInfo.gasPrice; - if (!lastNetworkGasPrice && networkGasPrice) { - lastNetworkGasPrice = networkGasPrice; - } - let range = networkGasPrice || lastNetworkGasPrice || fallbackGasPrice; - const gasPrice = transaction.gasPrice || range.initial; - // update gas price range according to previous pending transaction if necessary - const ethTransactionRaw = transactionRaw as EthereumTransactionRaw | undefined; - if (ethTransactionRaw && ethTransactionRaw.gasPrice) { - const gaspriceGap: number = getEnv("EDIT_TX_LEGACY_GASPRICE_GAP_SPEEDUP_FACTOR"); - const minNewGasPrice = new BigNumber(ethTransactionRaw.gasPrice).times(1 + gaspriceGap); - const minValue = BigNumber.max(range.min, minNewGasPrice); - let maxValue = BigNumber.max(range.max, minNewGasPrice); - // avoid lower bound = upper bound, which will cause an error in inferDynamicRange - if (minValue.isEqualTo(maxValue)) { - maxValue = minValue.times(2); - } - range = inferDynamicRange(minValue, { - minValue, - maxValue, - }); - } - const { units } = account.currency; - return ( - 1 ? units[1] : units[0]} - error={status.errors.gasPrice} - /> - ); -}; -export default FeesField; diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/MaxFeeField.tsx b/apps/ledger-live-desktop/src/renderer/families/ethereum/MaxFeeField.tsx deleted file mode 100644 index 1a518da2a72d..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/MaxFeeField.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import React, { useCallback, useMemo } from "react"; -import invariant from "invariant"; -import BigNumber from "bignumber.js"; -import styled from "styled-components"; -import { getEnv } from "@ledgerhq/live-env"; -import { useTranslation } from "react-i18next"; -import { - Transaction as EthereumTransaction, - TransactionRaw as EthereumTransactionRaw, -} from "@ledgerhq/live-common/families/ethereum/types"; -import { getMainAccount } from "@ledgerhq/live-common/account/index"; -import { getAccountBridge } from "@ledgerhq/live-common/bridge/index"; -import { formatCurrencyUnit } from "@ledgerhq/live-common/currencies/index"; -import { AccountBridge } from "@ledgerhq/types-live"; -import { track } from "~/renderer/analytics/segment"; -import Box from "~/renderer/components/Box"; -import InputCurrency from "~/renderer/components/InputCurrency"; -import Label from "~/renderer/components/Label"; -import LabelWithExternalIcon from "~/renderer/components/LabelWithExternalIcon"; -import TranslatedError from "~/renderer/components/TranslatedError"; -import { openURL } from "~/renderer/linking"; -import { urls } from "~/config/urls"; -import { MaxFeeTooLow } from "@ledgerhq/errors"; -import { EthereumFamily } from "./types"; - -const ErrorContainer = styled(Box)<{ hasError: Error }>` - margin-top: 0px; - font-size: 10px; - width: 100%; - transition: all 0.4s ease-in-out; - will-change: max-height; - max-height: ${p => (p.hasError ? 60 : 0)}px; - min-height: ${p => (p.hasError ? 22 : 0)}px; -`; - -const ErrorDisplay = styled(Box)` - color: ${p => p.theme.colors.pearl}; -`; - -const WarningDisplay = styled(Box)` - color: ${p => p.theme.colors.warning}; -`; - -const FeesValues = styled.span` - color: ${p => p.theme.colors.neutral.c90}; -`; - -const WhiteSpacedLabel = styled(Label)` - white-space: pre; - color: ${p => p.theme.colors.neutral.c60}; -`; - -const FeesField: NonNullable["component"] = ({ - account, - parentAccount, - transaction, - status, - updateTransaction, - transactionRaw, -}) => { - invariant(transaction.family === "ethereum", "FeeField: ethereum family expected"); - - const mainAccount = getMainAccount(account, parentAccount); - const bridge: AccountBridge = getAccountBridge(mainAccount); - const { t } = useTranslation(); - - const onMaxFeeChange = useCallback( - (maxFeePerGas: BigNumber) => - updateTransaction(() => - bridge.updateTransaction(transaction, { - maxFeePerGas, - feesStrategy: "custom", - }), - ), - [bridge, transaction, updateTransaction], - ); - - const defaultMaxFeePerGas = useMemo( - () => - transaction.networkInfo?.nextBaseFeePerGas - ?.times(getEnv("EIP1559_BASE_FEE_MULTIPLIER")) - .plus(transaction?.maxPriorityFeePerGas || 0) - .integerValue(), - [transaction.maxPriorityFeePerGas, transaction.networkInfo?.nextBaseFeePerGas], - ); - - const maxFeePerGas = transaction.maxFeePerGas || defaultMaxFeePerGas; - const { units } = mainAccount.currency; - const unit = units.length > 1 ? units[1] : units[0]; - const unitName = unit.code; - - const nextBaseFeeValue = useMemo( - () => - formatCurrencyUnit(unit, transaction.networkInfo?.nextBaseFeePerGas || new BigNumber(0), { - showCode: true, - disableRounding: true, - }), - [transaction.networkInfo?.nextBaseFeePerGas, unit], - ); - - // give user an error if maxFeePerGas is lower than pending transaction maxFeePerGas + 10% of pending transaction maxPriorityFeePerGas for edit eth transaction feature - const ethTransactionRaw = transactionRaw as EthereumTransactionRaw | undefined; - if ( - !status.errors.maxFee && - ethTransactionRaw && - ethTransactionRaw.maxPriorityFeePerGas && - ethTransactionRaw.maxFeePerGas - ) { - const maxPriorityFeeGap: number = getEnv("EDIT_TX_EIP1559_FEE_GAP_SPEEDUP_FACTOR"); - const lowerLimitMaxFeePerGas = new BigNumber(ethTransactionRaw.maxFeePerGas).times( - 1 + maxPriorityFeeGap, - ); - if (transaction.maxFeePerGas && transaction.maxFeePerGas.isLessThan(lowerLimitMaxFeePerGas)) { - status.errors.maxFee = new MaxFeeTooLow(); - } - } - const validTransactionError = status.errors.maxFee; - const validTransactionWarning = status.warnings.maxFee; - return ( - - { - openURL(urls.feesEIP1559MoreInfo); - track("Send Flow EIP1559 Fees Help Requested"); - }} - label={t("send.steps.details.ethereumMaxFee", { unitName })} - /> - - - - - {validTransactionError ? ( - - - - ) : validTransactionWarning ? ( - - - - ) : null} - - - {`${t("send.steps.details.nextBlock")} : `} - {nextBaseFeeValue} - - - ); -}; - -export default FeesField; diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/PriorityFeeField.tsx b/apps/ledger-live-desktop/src/renderer/families/ethereum/PriorityFeeField.tsx deleted file mode 100644 index ef7ab514b75a..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/PriorityFeeField.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import React, { useCallback, useMemo } from "react"; -import invariant from "invariant"; -import BigNumber from "bignumber.js"; -import styled from "styled-components"; -import { useTranslation } from "react-i18next"; -import { - Transaction as EthereumTransaction, - TransactionStatus, - TransactionRaw as EthereumTransactionRaw, -} from "@ledgerhq/live-common/families/ethereum/types"; -import { inferDynamicRange } from "@ledgerhq/live-common/range"; -import { getMainAccount } from "@ledgerhq/live-common/account/index"; -import { getAccountBridge } from "@ledgerhq/live-common/bridge/index"; -import { Result } from "@ledgerhq/live-common/bridge/useBridgeTransaction"; -import { Account, AccountBridge, AccountLike, TransactionCommonRaw } from "@ledgerhq/types-live"; -import { formatCurrencyUnit } from "@ledgerhq/live-common/currencies/index"; -import LabelWithExternalIcon from "~/renderer/components/LabelWithExternalIcon"; -import TranslatedError from "~/renderer/components/TranslatedError"; -import InputCurrency from "~/renderer/components/InputCurrency"; -import { track } from "~/renderer/analytics/segment"; -import Label from "~/renderer/components/Label"; -import { openURL } from "~/renderer/linking"; -import Box from "~/renderer/components/Box"; -import { urls } from "~/config/urls"; -import { getEnv } from "@ledgerhq/live-env"; -import { PriorityFeeTooLow } from "@ledgerhq/errors"; - -const ErrorContainer = styled(Box)<{ hasError?: boolean }>` - margin-top: 0px; - font-size: 10px; - width: 100%; - transition: all 0.4s ease-in-out; - will-change: max-height; - max-height: ${p => (p.hasError ? 60 : 0)}px; - min-height: ${p => (p.hasError ? 22 : 0)}px; -`; - -const ErrorDisplay = styled(Box)` - color: ${p => p.theme.colors.pearl}; -`; - -const WarningDisplay = styled(Box)` - color: ${p => p.theme.colors.warning}; -`; - -const FeesValues = styled.span` - color: ${p => p.theme.colors.neutral.c90}; -`; - -const WhiteSpacedLabel = styled(Label)` - white-space: pre; - color: ${p => p.theme.colors.neutral.c60}; -`; - -type Props = { - account: AccountLike; - parentAccount: Account | null | undefined; - transaction: EthereumTransaction; - status: TransactionStatus; - updateTransaction: Result["updateTransaction"]; - transactionRaw?: TransactionCommonRaw; -}; - -const fallbackMaxPriorityFeePerGas = inferDynamicRange(new BigNumber(10e9)); - -const FeesField = ({ - account, - parentAccount, - transaction, - status, - updateTransaction, - transactionRaw, -}: Props) => { - invariant(transaction.family === "ethereum", "FeeField: ethereum family expected"); - const mainAccount = getMainAccount(account, parentAccount); - - const bridge: AccountBridge = getAccountBridge(mainAccount); - const { t } = useTranslation(); - - const onPriorityFeeChange = useCallback( - (maxPriorityFeePerGas: BigNumber) => - updateTransaction(transaction => - bridge.updateTransaction(transaction, { - maxPriorityFeePerGas, - feesStrategy: "custom", - }), - ), - [updateTransaction, bridge], - ); - - const networkPriorityFee = useMemo( - () => transaction.networkInfo?.maxPriorityFeePerGas || fallbackMaxPriorityFeePerGas, - [transaction.networkInfo], - ); - - const maxPriorityFee = transaction.maxPriorityFeePerGas || fallbackMaxPriorityFeePerGas.initial; - const { units } = mainAccount.currency; - const unit = units.length > 1 ? units[1] : units[0]; - const unitName = unit.code; - - let [lowPriorityFeeValue, highPriorityFeeValue] = useMemo( - () => [ - formatCurrencyUnit(unit, networkPriorityFee.min), - formatCurrencyUnit(unit, networkPriorityFee.max), - ], - [networkPriorityFee.max, networkPriorityFee.min, unit], - ); - - // update suggested max priority fee according to previous pending transaction if necessary - const ethTransactionRaw = transactionRaw as EthereumTransactionRaw | undefined; - if (ethTransactionRaw && ethTransactionRaw.maxPriorityFeePerGas) { - // update the range to make sure that new maxPriorityFeePerGas is at least 10% higher than the pending transaction - const maxPriorityFeeGap: number = getEnv("EDIT_TX_EIP1559_FEE_GAP_SPEEDUP_FACTOR"); - const newMaxPriorityFeePerGas = new BigNumber(ethTransactionRaw.maxPriorityFeePerGas).times( - 1 + maxPriorityFeeGap, - ); - const newMaxPriorityFeePerGasinGwei = formatCurrencyUnit(unit, newMaxPriorityFeePerGas); - if ( - new BigNumber(newMaxPriorityFeePerGasinGwei).isGreaterThan(new BigNumber(lowPriorityFeeValue)) - ) { - lowPriorityFeeValue = newMaxPriorityFeePerGasinGwei; - } - if ( - new BigNumber(newMaxPriorityFeePerGasinGwei).isGreaterThan( - new BigNumber(highPriorityFeeValue), - ) - ) { - highPriorityFeeValue = newMaxPriorityFeePerGasinGwei; - } - // give user an error if maxPriorityFeePerGas is lower than pending transaction maxPriorityFeePerGas + 10% for edit eth transaction feature - if ( - !status.errors.maxPriorityFee && - transaction.maxPriorityFeePerGas && - transaction.maxPriorityFeePerGas.isLessThan(newMaxPriorityFeePerGas) - ) { - status.errors.maxPriorityFee = new PriorityFeeTooLow(); - } - } - - const validTransactionError = status.errors.maxPriorityFee; - const validTransactionWarning = status.warnings.maxPriorityFee; - - return ( - - { - openURL(urls.feesEIP1559MoreInfo); - track("Send Flow EIP1559 Fees Help Requested"); - }} - label={t("send.steps.details.ethereumPriorityFee", { unitName })} - /> - - - - - {validTransactionError ? ( - - - - ) : validTransactionWarning ? ( - - - - ) : null} - - - {`${t("send.steps.details.suggested")} : `} - - {lowPriorityFeeValue} - {highPriorityFeeValue} {unitName} - - - - ); -}; - -export default FeesField; diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/SelectFeeStrategy.tsx b/apps/ledger-live-desktop/src/renderer/families/ethereum/SelectFeeStrategy.tsx deleted file mode 100644 index ff547e41b97c..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/SelectFeeStrategy.tsx +++ /dev/null @@ -1,239 +0,0 @@ -import React, { memo } from "react"; -import styled from "styled-components"; -import { BigNumber } from "bignumber.js"; -import { - getAccountCurrency, - getAccountUnit, - getMainAccount, -} from "@ledgerhq/live-common/account/index"; -import { useTranslation, Trans } from "react-i18next"; -import { Account, AccountLike, FeeStrategy, TransactionCommonRaw } from "@ledgerhq/types-live"; -import { - Transaction as EthereumTransaction, - TransactionRaw as EthereumTransactionRaw, -} from "@ledgerhq/live-common/families/ethereum/types"; -import TachometerMedium from "~/renderer/icons/TachometerMedium"; -import CounterValue from "~/renderer/components/CounterValue"; -import FormattedVal from "~/renderer/components/FormattedVal"; -import TachometerHigh from "~/renderer/icons/TachometerHigh"; -import TachometerLow from "~/renderer/icons/TachometerLow"; -import Box, { Tabbable } from "~/renderer/components/Box"; -import Text from "~/renderer/components/Text"; -import Clock from "~/renderer/icons/Clock"; -import { EIP1559ShouldBeUsed } from "@ledgerhq/live-common/families/ethereum/transaction"; -import { getEnv } from "@ledgerhq/live-env"; - -type OnClickType = { - amount: BigNumber; - feesStrategy: string; - extra?: Record; -}; - -type Props = { - onClick: (arg: OnClickType) => void; - transaction: EthereumTransaction; - account: AccountLike; - parentAccount: Account | null | undefined; - strategies: FeeStrategy[]; - mapStrategies?: (arg: FeeStrategy) => FeeStrategy; - suffixPerByte?: boolean; - transactionRaw?: TransactionCommonRaw; -}; - -const FeesWrapper = styled(Tabbable)` - flex-direction: column; - align-items: center; - justify-content: space-between; - gap: 6px; - - border: ${p => - `1px solid ${ - p?.selected ? p.theme.colors.palette.primary.main : p.theme.colors.palette.divider - }`}; - padding: 20px 16px; - font-family: "Inter"; - border-radius: 4px; - width: 140px; - ${p => (p.disabled ? `background: ${p.theme.colors.palette.background.default};` : "")}; - - &:hover { - cursor: ${p => (p.disabled ? "unset" : "pointer")}; - } -`; - -const FeesHeader = styled(Box)<{ selected?: boolean; disabled?: boolean }>` - color: ${p => - p.selected - ? p.theme.colors.palette.primary.main - : p.disabled - ? p.theme.colors.palette.text.shade20 - : p.theme.colors.palette.text.shade50}; -`; - -const FeesValue = styled(Box)` - flex-direction: column; - align-items: center; - text-align: center; -`; - -const ApproximateTransactionTime = styled(Box)<{ selected?: boolean }>` - flex-direction: row; - align-items: center; - border-radius: 3px; - background-color: ${p => - p.selected ? p.theme.colors.palette.primary.main : p.theme.colors.palette.text.shade20}; - padding: 5px 6px; -`; - -const SelectFeeStrategy = ({ - transaction, - account, - parentAccount, - onClick, - strategies, - mapStrategies, - suffixPerByte, - transactionRaw, -}: Props) => { - const mainAccount = getMainAccount(account, parentAccount); - const accountUnit = getAccountUnit(mainAccount); - const feesCurrency = getAccountCurrency(mainAccount); - const { t } = useTranslation(); - strategies = mapStrategies ? strategies.map(mapStrategies) : strategies; - if (transactionRaw) { - // disable low transaction fee options in case of edit transaction modal - const ethTransactionRaw = transactionRaw as EthereumTransactionRaw; - if (EIP1559ShouldBeUsed(mainAccount.currency)) { - const oldMaxPriorityFeePerGas = ethTransactionRaw.maxPriorityFeePerGas; - const oldMaxFeePerGas = ethTransactionRaw.maxFeePerGas; - const feesGap: number = getEnv("EDIT_TX_EIP1559_FEE_GAP_SPEEDUP_FACTOR"); - strategies.forEach(strategy => { - const strategyMaxPriorityFeePerGas = strategy.extra?.maxPriorityFeePerGas; - const strategyMaxFeePerGas = strategy.extra?.maxFeePerGas; - if ( - oldMaxPriorityFeePerGas && - strategyMaxPriorityFeePerGas && - oldMaxFeePerGas && - strategyMaxFeePerGas - ) { - // MaxPriorityFee should be at least 10% higher than the old one - // MaxFee should be at least old MaxFee + 10% of old MaxPriorityFee - strategy.disabled = - strategy.disabled || - strategyMaxPriorityFeePerGas.isLessThan( - BigNumber(oldMaxPriorityFeePerGas).times(1 + feesGap), - ) || - strategyMaxFeePerGas.isLessThan(BigNumber(oldMaxFeePerGas).times(1 + feesGap)); - } - }); - } else { - const gaspriceGap: number = getEnv("EDIT_TX_LEGACY_GASPRICE_GAP_SPEEDUP_FACTOR"); - const oldGasPrice = ethTransactionRaw.gasPrice; - if (oldGasPrice) { - strategies.forEach(strategy => { - strategy.disabled = - strategy.disabled || - strategy.amount.isLessThan(BigNumber(oldGasPrice).times(1 + gaspriceGap)); - }); - } - } - } - - return ( - - {strategies.map(strategy => { - const amount = strategy.displayedAmount || strategy.amount; - const { label, disabled } = strategy; - const selected = transaction.feesStrategy === strategy.label && !disabled; - return ( - { - !disabled && - onClick({ - amount: strategy.amount, - feesStrategy: label, - extra: strategy.extra, - }); - }} - > - <> - - {label === "medium" ? ( - - ) : label === "slow" ? ( - - ) : ( - - )} - - - - - - - {strategy.displayedAmount ? ( - - ) : null} - - {feesCurrency.id === "ethereum" && ( - - - - {label === "medium" ? ( - <> - ≈ 30 - - ) : label === "slow" ? ( - <> - ≈ 2-3 - - ) : ( - <> - ≈ 15 - - )} - - - )} - - - ); - })} - - ); -}; - -export default memo(SelectFeeStrategy); diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/SendAmountFields.tsx b/apps/ledger-live-desktop/src/renderer/families/ethereum/SendAmountFields.tsx deleted file mode 100644 index c26052aed349..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/SendAmountFields.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { getAccountBridge } from "@ledgerhq/live-common/bridge/index"; -import { useFeesStrategy } from "@ledgerhq/live-common/families/ethereum/react"; -import { EIP1559ShouldBeUsed } from "@ledgerhq/live-common/families/ethereum/transaction"; -import { Transaction as EthereumTransaction } from "@ledgerhq/live-common/families/ethereum/types"; -import { AccountBridge } from "@ledgerhq/types-live"; -import React, { useCallback, useState } from "react"; -import SendFeeMode from "~/renderer/components/SendFeeMode"; -import { ContextValue, context } from "~/renderer/drawers/Provider"; -import GasLimitField from "./GasLimitField"; -import GasPriceField from "./GasPriceField"; -import MaxFeeField from "./MaxFeeField"; -import PriorityFeeField from "./PriorityFeeField"; -import SelectFeeStrategy from "./SelectFeeStrategy"; -import { EthereumFamily } from "./types"; - -const Root: NonNullable["component"] = props => { - const { account, updateTransaction, transaction } = props; - const bridge: AccountBridge = getAccountBridge(account); - const { state: drawerState, setDrawer } = React.useContext(context); - - const defaultStrategies = useFeesStrategy(transaction); - const [isAdvanceMode, setAdvanceMode] = useState(!transaction.feesStrategy); - const strategies = defaultStrategies; - - const onFeeStrategyClick = useCallback( - ({ amount, feesStrategy, extra }) => { - updateTransaction((tx: EthereumTransaction) => - bridge.updateTransaction(tx, { - gasPrice: amount, - maxFeePerGas: extra?.maxFeePerGas, - maxPriorityFeePerGas: extra?.maxPriorityFeePerGas, - feesStrategy, - }), - ); - if (drawerState.open) setDrawer(undefined); - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [updateTransaction, bridge], - ); - - return ( - <> - - {isAdvanceMode ? ( - EIP1559ShouldBeUsed(account.currency) ? ( - <> - - - - - ) : ( - <> - - - - ) - ) : ( - <> - - - )} - - ); -}; - -export default { - component: Root, - fields: ["feeStrategy", "gasLimit", "gasPrice"], -}; diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/helpers.ts b/apps/ledger-live-desktop/src/renderer/families/ethereum/helpers.ts deleted file mode 100644 index 0f5c1e1f5d02..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/helpers.ts +++ /dev/null @@ -1,37 +0,0 @@ -import BigNumber from "bignumber.js"; -import { - Transaction as EthereumTransaction, - TransactionMode, -} from "@ledgerhq/live-common/families/ethereum/types"; -import { Account, AnyMessage, NFTStandard } from "@ledgerhq/types-live"; -import { getEIP712FieldsDisplayedOnNano } from "@ledgerhq/evm-tools/message/EIP712/index"; -import { MessageProperties, NftProperties } from "../types"; -import { getEnv } from "@ledgerhq/live-env"; - -export const getNftTransactionProperties = (transaction: EthereumTransaction): NftProperties => ({ - tokenId: transaction?.tokenIds?.[0] || null, - contract: transaction?.collection || null, - quantity: transaction?.quantities?.[0] || null, -}); - -export const injectNftIntoTransaction = ( - transaction: EthereumTransaction, - nftProperties: Partial, - standard?: NFTStandard, -): EthereumTransaction => ({ - ...transaction, - mode: standard ? (`${standard.toLowerCase()}.transfer` as TransactionMode) : transaction.mode, - collection: nftProperties?.contract ?? transaction?.collection ?? "", - tokenIds: [nftProperties?.tokenId ?? transaction.tokenIds?.[0] ?? ""], - quantities: [nftProperties?.quantity ?? transaction.quantities?.[0] ?? new BigNumber(NaN)], -}); - -export const getMessageProperties = async ( - account: Account, - messageData: AnyMessage, -): Promise => { - if (messageData.standard === "EIP712") { - return getEIP712FieldsDisplayedOnNano(messageData.message, getEnv("DYNAMIC_CAL_BASE_URL")); - } - return null; -}; diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/index.ts b/apps/ledger-live-desktop/src/renderer/families/ethereum/index.ts deleted file mode 100644 index 1906688830d1..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -import accountHeaderManageActions from "./AccountHeaderManageActions"; -import AccountBodyHeader from "./AccountBodyHeader"; -import sendAmountFields from "./SendAmountFields"; -import StakeBanner from "./StakeBanner"; -import transactionConfirmFields from "./TransactionConfirmFields"; -import operationDetails from "./operationDetails"; -import AccountFooter from "./AccountFooter"; -import StepSummaryNetworkFeesRow from "./StepSummaryNetworkFeesRow"; -import { - getNftTransactionProperties, - injectNftIntoTransaction, - getMessageProperties, -} from "./helpers"; - -import { EthereumFamily } from "./types"; - -const family: EthereumFamily = { - AccountFooter, - operationDetails, - accountHeaderManageActions, - transactionConfirmFields, - AccountBodyHeader, - sendAmountFields, - StepSummaryNetworkFeesRow, - StakeBanner, - nft: { - getNftTransactionProperties, - injectNftIntoTransaction, - }, - message: { - getMessageProperties, - }, -}; - -export default family; diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/types.ts b/apps/ledger-live-desktop/src/renderer/families/ethereum/types.ts deleted file mode 100644 index 1152fcc1214f..000000000000 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Transaction, TransactionStatus } from "@ledgerhq/live-common/families/ethereum/types"; - -import { Account, Operation } from "@ledgerhq/types-live"; -import { LLDCoinFamily } from "../types"; - -export type EthereumFamily = LLDCoinFamily; diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/AccountFooter.tsx b/apps/ledger-live-desktop/src/renderer/families/evm/AccountFooter.tsx similarity index 99% rename from apps/ledger-live-desktop/src/renderer/families/ethereum/AccountFooter.tsx rename to apps/ledger-live-desktop/src/renderer/families/evm/AccountFooter.tsx index b242d3aa105d..3fcadc118b2d 100644 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/AccountFooter.tsx +++ b/apps/ledger-live-desktop/src/renderer/families/evm/AccountFooter.tsx @@ -21,6 +21,7 @@ type Props = { parentAccount?: Account | undefined | null; status: TransactionStatus; }; + const AccountFooter = ({ account, parentAccount, status }: Props) => { const currency = getAccountCurrency(account); const mainAccount = getMainAccount(account, parentAccount); @@ -76,4 +77,5 @@ const AccountFooter = ({ account, parentAccount, status }: Props) => { ); }; + export default AccountFooter; diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/AccountHeaderManageActions.ts b/apps/ledger-live-desktop/src/renderer/families/evm/AccountHeaderManageActions.ts similarity index 91% rename from apps/ledger-live-desktop/src/renderer/families/ethereum/AccountHeaderManageActions.ts rename to apps/ledger-live-desktop/src/renderer/families/evm/AccountHeaderManageActions.ts index 90e7689dfa89..445537747cda 100644 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/AccountHeaderManageActions.ts +++ b/apps/ledger-live-desktop/src/renderer/families/evm/AccountHeaderManageActions.ts @@ -5,13 +5,16 @@ import { useTranslation } from "react-i18next"; import IconCoins from "~/renderer/icons/ClaimReward"; import { openModal } from "~/renderer/actions/modals"; import { isAccountEmpty } from "@ledgerhq/live-common/account/index"; + type Props = { account: AccountLike; parentAccount: Account | undefined | null; }; + const AccountHeaderActions = ({ account, parentAccount }: Props) => { const { t } = useTranslation(); const dispatch = useDispatch(); + const onClick = useCallback(() => { if (isAccountEmpty(account)) { dispatch( @@ -22,13 +25,14 @@ const AccountHeaderActions = ({ account, parentAccount }: Props) => { ); } else if (account.type === "Account") { dispatch( - openModal("MODAL_ETH_STAKE", { + openModal("MODAL_EVM_STAKE", { account, }), ); } }, [account, dispatch, parentAccount]); - if (account.type === "Account" && account.currency.id === "ethereum") { + + if (account.type === "Account" && account.currency.id.includes("ethereum")) { return [ { key: "Stake", @@ -48,4 +52,5 @@ const AccountHeaderActions = ({ account, parentAccount }: Props) => { return []; } }; + export default AccountHeaderActions; diff --git a/apps/ledger-live-desktop/src/renderer/families/evm/SendAmountFields/GasPriceField.tsx b/apps/ledger-live-desktop/src/renderer/families/evm/SendAmountFields/GasPriceField.tsx index c6497337d0f6..d0b2c74f4676 100644 --- a/apps/ledger-live-desktop/src/renderer/families/evm/SendAmountFields/GasPriceField.tsx +++ b/apps/ledger-live-desktop/src/renderer/families/evm/SendAmountFields/GasPriceField.tsx @@ -47,6 +47,7 @@ const FeesField: NonNullable["component"] = ({ const range = networkGasPrice || lastNetworkGasPrice; const gasPrice = transaction.gasPrice || range.initial; const { units } = account.currency; + return ( ["component"] = ({ /> ); }; + export default memo(FeesField); diff --git a/apps/ledger-live-desktop/src/renderer/families/evm/SendAmountFields/SelectFeeStrategy.tsx b/apps/ledger-live-desktop/src/renderer/families/evm/SendAmountFields/SelectFeeStrategy.tsx index 12e143dfa055..b966aba54d09 100644 --- a/apps/ledger-live-desktop/src/renderer/families/evm/SendAmountFields/SelectFeeStrategy.tsx +++ b/apps/ledger-live-desktop/src/renderer/families/evm/SendAmountFields/SelectFeeStrategy.tsx @@ -76,9 +76,7 @@ const SelectFeeStrategy = ({ transaction, account, onClick, gasOptions }: Props) () => strategies.map(strategy => { const selected = transaction.feesStrategy === strategy; - const gasOption = gasOptions[strategy]; - const estimatedFees = getEstimatedFees(getTypedTransaction(transaction, gasOption)); return ( diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/StakeBanner.tsx b/apps/ledger-live-desktop/src/renderer/families/evm/StakeBanner.tsx similarity index 73% rename from apps/ledger-live-desktop/src/renderer/families/ethereum/StakeBanner.tsx rename to apps/ledger-live-desktop/src/renderer/families/evm/StakeBanner.tsx index cb4a8b8a7d19..55c338197336 100644 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/StakeBanner.tsx +++ b/apps/ledger-live-desktop/src/renderer/families/evm/StakeBanner.tsx @@ -2,15 +2,24 @@ import { Account } from "@ledgerhq/types-live"; import { useHistory } from "react-router"; import { useTranslation } from "react-i18next"; import useFeature from "@ledgerhq/live-common/featureFlags/useFeature"; -import { getAccountBannerState as getEthereumBannerState } from "@ledgerhq/live-common/families/ethereum/banner"; +import { getAccountBannerState } from "@ledgerhq/live-common/families/evm/banner"; import { AccountBanner } from "~/renderer/screens/account/AccountBanner"; import { track } from "~/renderer/analytics/segment"; import { stakeDefaultTrack } from "~/renderer/screens/stake/constants"; import React from "react"; import { StakeAccountBannerParams } from "~/renderer/screens/account/types"; +import { urls } from "~/config/urls"; -const kilnAppId = "kiln"; -const lidoAppId = "lido"; +/** + * This needs to stay a constant because it serves a different from + * type AccountBannerState["stakeProvider"] possible values. + * Type AccountBannerState["stakeProvider"] is internal to LL and used to + * differenciate available stake providers. + * Whereas KILN_LIVE_APP_ID and LIDO_LIVE_APP_ID are ids of the related LiveApps + * Today they might be the same but it's not guaranteed to stay like this. + */ +const KILN_LIVE_APP_ID = "kiln"; +const LIDO_LIVE_APP_ID = "lido"; const StakeBanner: React.FC<{ account: Account }> = ({ account }) => { const history = useHistory(); @@ -19,11 +28,11 @@ const StakeBanner: React.FC<{ account: Account }> = ({ account }) => { const ethStakingProviders = useFeature("ethStakingProviders"); const stakeAccountBannerParams: StakeAccountBannerParams | null = stakeAccountBanner?.params ?? null; - const state = getEthereumBannerState(account); - const { stakeProvider } = state; + const { stakeProvider } = getAccountBannerState(account); if (stakeProvider === "lido" && !stakeAccountBannerParams?.eth?.lido) return null; if (stakeProvider === "kiln" && !stakeAccountBannerParams?.eth?.kiln) return null; + const stakeOnClick = (providerLiveAppId: string) => { track("button_clicked", { ...stakeDefaultTrack, @@ -53,23 +62,27 @@ const StakeBanner: React.FC<{ account: Account }> = ({ account }) => { : t("account.banner.delegation.ethereum.lido.cta"); const onClick = - stakeProvider === "kiln" ? () => stakeOnClick(kilnAppId) : () => stakeOnClick(lidoAppId); + stakeProvider === "kiln" + ? () => stakeOnClick(KILN_LIVE_APP_ID) + : () => stakeOnClick(LIDO_LIVE_APP_ID); const display = ethStakingProviders?.params?.listProvider?.some( (provider: { liveAppId: string }) => provider.liveAppId === stakeProvider, ); - if (!display) return null; + if (!display) { + return null; + } return ( ); }; diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/StakeFlowModal/EthStakingModalBody.tsx b/apps/ledger-live-desktop/src/renderer/families/evm/StakeFlowModal/EthStakingModalBody.tsx similarity index 98% rename from apps/ledger-live-desktop/src/renderer/families/ethereum/StakeFlowModal/EthStakingModalBody.tsx rename to apps/ledger-live-desktop/src/renderer/families/evm/StakeFlowModal/EthStakingModalBody.tsx index e68f0b611c19..d18b07b72657 100644 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/StakeFlowModal/EthStakingModalBody.tsx +++ b/apps/ledger-live-desktop/src/renderer/families/evm/StakeFlowModal/EthStakingModalBody.tsx @@ -1,23 +1,23 @@ -import React, { useCallback, useState } from "react"; +import { Flex, Text } from "@ledgerhq/react-ui"; import { Account } from "@ledgerhq/types-live"; +import React, { useCallback, useState } from "react"; import { useHistory } from "react-router-dom"; -import { Flex, Text } from "@ledgerhq/react-ui"; -import { track } from "~/renderer/analytics/segment"; +import { appendQueryParamsToDappURL } from "@ledgerhq/live-common/platform/utils/appendQueryParamsToDappURL"; import { useTranslation } from "react-i18next"; +import { track } from "~/renderer/analytics/segment"; +import CheckBox from "~/renderer/components/CheckBox"; +import EthStakeIllustration from "~/renderer/icons/EthStakeIllustration"; import { openURL } from "~/renderer/linking"; import { - LOCAL_STORAGE_KEY_PREFIX, CheckBoxContainer, + LOCAL_STORAGE_KEY_PREFIX, } from "~/renderer/modals/Receive/steps/StepReceiveStakingFlow"; -import CheckBox from "~/renderer/components/CheckBox"; -import { appendQueryParamsToDappURL } from "@ledgerhq/live-common/platform/utils/appendQueryParamsToDappURL"; -import EthStakeIllustration from "~/renderer/icons/EthStakeIllustration"; import { ListProvider, ListProviders } from "./types"; import { getTrackProperties } from "./utils/getTrackProperties"; -import ProviderItem from "./component/ProviderItem"; import { LiveAppManifest } from "@ledgerhq/live-common/platform/types"; +import ProviderItem from "./component/ProviderItem"; type Props = { account: Account; @@ -116,7 +116,7 @@ export function EthStakingModalBody({ {listProviders.map(item => ( - + { + +const StakingModal = ({ account, hasCheckbox, singleProviderRedirectMode, source }: Props) => { const ethStakingProviders = useFeature("ethStakingProviders"); - if (!ethStakingProviders?.enabled) return null; + if (!ethStakingProviders?.enabled) { + return null; + } return ( ( @@ -44,4 +47,5 @@ const DelegationModal = ({ account, hasCheckbox, singleProviderRedirectMode, sou /> ); }; -export default DelegationModal; + +export default StakingModal; diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/StakeFlowModal/types.ts b/apps/ledger-live-desktop/src/renderer/families/evm/StakeFlowModal/types.ts similarity index 100% rename from apps/ledger-live-desktop/src/renderer/families/ethereum/StakeFlowModal/types.ts rename to apps/ledger-live-desktop/src/renderer/families/evm/StakeFlowModal/types.ts diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/StakeFlowModal/utils/getTrackProperties.ts b/apps/ledger-live-desktop/src/renderer/families/evm/StakeFlowModal/utils/getTrackProperties.ts similarity index 100% rename from apps/ledger-live-desktop/src/renderer/families/ethereum/StakeFlowModal/utils/getTrackProperties.ts rename to apps/ledger-live-desktop/src/renderer/families/evm/StakeFlowModal/utils/getTrackProperties.ts diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/StepSummaryNetworkFeesRow.tsx b/apps/ledger-live-desktop/src/renderer/families/evm/StepSummaryNetworkFeesRow.tsx similarity index 90% rename from apps/ledger-live-desktop/src/renderer/families/ethereum/StepSummaryNetworkFeesRow.tsx rename to apps/ledger-live-desktop/src/renderer/families/evm/StepSummaryNetworkFeesRow.tsx index 09ce6328edcc..8f135d0271c6 100644 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/StepSummaryNetworkFeesRow.tsx +++ b/apps/ledger-live-desktop/src/renderer/families/evm/StepSummaryNetworkFeesRow.tsx @@ -1,5 +1,6 @@ import React from "react"; import { Trans, useTranslation } from "react-i18next"; +import { useTheme } from "styled-components"; import Box from "~/renderer/components/Box"; import CounterValue from "~/renderer/components/CounterValue"; import FormattedVal from "~/renderer/components/FormattedVal"; @@ -17,6 +18,7 @@ const StepSummaryNetworkFeesRow = ({ feesCurrency, }: SummaryNetworkFeesRowProps) => { const { t } = useTranslation(); + const { colors } = useTheme(); return ( <> @@ -29,7 +31,7 @@ const StepSummaryNetworkFeesRow = ({ { switch (transaction.mode) { - case "erc20.approve": + case "send": return ( ); + default: return transaction.data ? ( @@ -34,6 +35,7 @@ const Warning = ({ ); } }; + export default { warning: Warning, }; diff --git a/apps/ledger-live-desktop/src/renderer/families/evm/index.ts b/apps/ledger-live-desktop/src/renderer/families/evm/index.ts index a0b7e51c1d1e..eeec9876c968 100644 --- a/apps/ledger-live-desktop/src/renderer/families/evm/index.ts +++ b/apps/ledger-live-desktop/src/renderer/families/evm/index.ts @@ -1,13 +1,22 @@ -import { - getMessageProperties, - getNftTransactionProperties, - injectNftIntoTransaction, -} from "./helpers"; +import { getMessageProperties } from "@ledgerhq/coin-evm/logic"; +import AccountFooter from "./AccountFooter"; +import accountHeaderManageActions from "./AccountHeaderManageActions"; import sendAmountFields from "./SendAmountFields"; +import StakeBanner from "./StakeBanner"; +import StepSummaryNetworkFeesRow from "./StepSummaryNetworkFeesRow"; +import transactionConfirmFields from "./TransactionConfirmFields"; +import { getNftTransactionProperties, injectNftIntoTransaction } from "./nft"; +import operationDetails from "./operationDetails"; import { EvmFamily } from "./types"; const family: EvmFamily = { + AccountFooter, + operationDetails, + accountHeaderManageActions, + transactionConfirmFields, sendAmountFields, + StepSummaryNetworkFeesRow, + StakeBanner, nft: { getNftTransactionProperties, injectNftIntoTransaction, diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/modals.ts b/apps/ledger-live-desktop/src/renderer/families/evm/modals.ts similarity index 55% rename from apps/ledger-live-desktop/src/renderer/families/ethereum/modals.ts rename to apps/ledger-live-desktop/src/renderer/families/evm/modals.ts index ce05281c7cf3..ef36f6ec74b2 100644 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/modals.ts +++ b/apps/ledger-live-desktop/src/renderer/families/evm/modals.ts @@ -1,21 +1,18 @@ import { MakeModalsType } from "~/renderer/modals/types"; -import MODAL_ETH_STAKE from "./StakeFlowModal"; -import MODAL_EDIT_TRANSACTION, { Props as EditTransactionProps } from "./EditTransactionModal"; +import MODAL_EVM_STAKE from "./StakeFlowModal"; import { Account } from "@ledgerhq/types-live"; export type ModalsData = { - MODAL_ETH_STAKE: { + MODAL_EVM_STAKE: { account: Account; hasCheckbox?: boolean; singleProviderRedirectMode?: boolean; source?: string; }; - MODAL_EDIT_TRANSACTION: EditTransactionProps; }; const modals: MakeModalsType = { - MODAL_ETH_STAKE, - MODAL_EDIT_TRANSACTION, + MODAL_EVM_STAKE, }; export default modals; diff --git a/apps/ledger-live-desktop/src/renderer/families/evm/helpers.ts b/apps/ledger-live-desktop/src/renderer/families/evm/nft.ts similarity index 62% rename from apps/ledger-live-desktop/src/renderer/families/evm/helpers.ts rename to apps/ledger-live-desktop/src/renderer/families/evm/nft.ts index 42555f1158c8..515b32eb357c 100644 --- a/apps/ledger-live-desktop/src/renderer/families/evm/helpers.ts +++ b/apps/ledger-live-desktop/src/renderer/families/evm/nft.ts @@ -1,9 +1,7 @@ import BigNumber from "bignumber.js"; -import { getEnv } from "@ledgerhq/live-env"; -import { NFTStandard, AnyMessage, Account } from "@ledgerhq/types-live"; +import { NFTStandard } from "@ledgerhq/types-live"; import { Transaction as EvmTransaction } from "@ledgerhq/coin-evm/types/index"; -import { getEIP712FieldsDisplayedOnNano } from "@ledgerhq/evm-tools/message/EIP712/index"; -import { NftProperties, MessageProperties } from "../types"; +import { NftProperties } from "../types"; export const getNftTransactionProperties = (transaction: EvmTransaction): NftProperties => ({ tokenId: transaction.nft?.tokenId || null, @@ -25,13 +23,3 @@ export const injectNftIntoTransaction = ( quantity: nftProperties?.quantity ?? transaction.nft?.quantity ?? new BigNumber(NaN), }, }); - -export const getMessageProperties = async ( - account: Account, - messageData: AnyMessage, -): Promise => { - if (messageData.standard === "EIP712") { - return getEIP712FieldsDisplayedOnNano(messageData.message, getEnv("DYNAMIC_CAL_BASE_URL")); - } - return null; -}; diff --git a/apps/ledger-live-desktop/src/renderer/families/ethereum/operationDetails.tsx b/apps/ledger-live-desktop/src/renderer/families/evm/operationDetails.tsx similarity index 94% rename from apps/ledger-live-desktop/src/renderer/families/ethereum/operationDetails.tsx rename to apps/ledger-live-desktop/src/renderer/families/evm/operationDetails.tsx index e9f583f7d552..28267cd70f90 100644 --- a/apps/ledger-live-desktop/src/renderer/families/ethereum/operationDetails.tsx +++ b/apps/ledger-live-desktop/src/renderer/families/evm/operationDetails.tsx @@ -8,9 +8,6 @@ import Box from "~/renderer/components/Box"; import Skeleton from "~/renderer/components/Nft/Skeleton"; import Text from "~/renderer/components/Text"; -type Props = { - operation: Operation; -}; const Cell = styled(Box).attrs(() => ({ horizontal: false, alignItems: "flex-end", @@ -20,7 +17,12 @@ const Cell = styled(Box).attrs(() => ({ justify-content: center; min-width: 150px; `; -const NFTAmountField = ({ operation }: Props) => { + +type NFTAmountFieldProps = { + operation: Operation; +}; + +const NFTAmountField = ({ operation }: NFTAmountFieldProps) => { const { currencyId } = decodeAccountId(operation.accountId); const { status, metadata } = useNftMetadata(operation.contract, operation.tokenId, currencyId); const show = useMemo(() => status === "loading", [status]); @@ -39,10 +41,12 @@ const NFTAmountField = ({ operation }: Props) => { ); }; + const amountCell = { NFT_OUT: NFTAmountField, NFT_IN: NFTAmountField, }; + export default { amountCell, }; diff --git a/apps/ledger-live-desktop/src/renderer/families/evm/types.ts b/apps/ledger-live-desktop/src/renderer/families/evm/types.ts index b04a8c02cebc..002e9f49dfd8 100644 --- a/apps/ledger-live-desktop/src/renderer/families/evm/types.ts +++ b/apps/ledger-live-desktop/src/renderer/families/evm/types.ts @@ -1,5 +1,4 @@ import { Transaction, TransactionStatus } from "@ledgerhq/coin-evm/types/index"; - import { Account, Operation } from "@ledgerhq/types-live"; import { LLDCoinFamily } from "../types"; diff --git a/apps/ledger-live-desktop/src/renderer/families/types.ts b/apps/ledger-live-desktop/src/renderer/families/types.ts index 9e164493e413..2f18244fbae5 100644 --- a/apps/ledger-live-desktop/src/renderer/families/types.ts +++ b/apps/ledger-live-desktop/src/renderer/families/types.ts @@ -14,6 +14,7 @@ import { SubAccount, TransactionCommon, TransactionCommonRaw, + MessageProperties, } from "@ledgerhq/types-live"; // FIXME: ideally we need to have parametric version of StepProps import { StepProps as SendStepProps } from "../modals/Send/types"; @@ -308,7 +309,7 @@ export type LLDCoinFamily< }; message?: { - getMessageProperties: (account: A, message: AnyMessage) => Promise; + getMessageProperties: (message: AnyMessage) => Promise; }; }; @@ -347,8 +348,3 @@ export type NftProperties = { contract: string | null; quantity: BigNumber | null; }; - -export type MessageProperties = { - label: string; - value: string | string[]; -}[]; diff --git a/apps/ledger-live-desktop/src/renderer/modals/Receive/steps/StepReceiveFunds.tsx b/apps/ledger-live-desktop/src/renderer/modals/Receive/steps/StepReceiveFunds.tsx index 50bf2ab50c7b..9fcdd8c138b4 100644 --- a/apps/ledger-live-desktop/src/renderer/modals/Receive/steps/StepReceiveFunds.tsx +++ b/apps/ledger-live-desktop/src/renderer/modals/Receive/steps/StepReceiveFunds.tsx @@ -217,7 +217,7 @@ const StepReceiveFunds = (props: StepProps) => { }); if (receiveStakingFlowConfig?.params?.[id]?.direct) { dispatch( - openModal("MODAL_ETH_STAKE", { + openModal("MODAL_EVM_STAKE", { account: mainAccount, hasCheckbox: true, singleProviderRedirectMode: false, diff --git a/apps/ledger-live-desktop/src/renderer/modals/Send/AccountFooter.tsx b/apps/ledger-live-desktop/src/renderer/modals/Send/AccountFooter.tsx index a1b0c7963493..231f62e73f03 100644 --- a/apps/ledger-live-desktop/src/renderer/modals/Send/AccountFooter.tsx +++ b/apps/ledger-live-desktop/src/renderer/modals/Send/AccountFooter.tsx @@ -14,11 +14,13 @@ import FormattedVal from "~/renderer/components/FormattedVal"; import Label from "~/renderer/components/Label"; import CounterValue from "~/renderer/components/CounterValue"; import { getLLDCoinFamily } from "~/renderer/families"; + type Props = { account: AccountLike; parentAccount?: Account | undefined | null; status: TransactionStatus; }; + const AccountFooter = ({ account, parentAccount, status }: Props) => { const currency = getAccountCurrency(account); const mainAccount = getMainAccount(account, parentAccount); @@ -28,6 +30,7 @@ const AccountFooter = ({ account, parentAccount, status }: Props) => { const cryptoCurrency = mainAccount.currency; const specific = cryptoCurrency ? getLLDCoinFamily(cryptoCurrency.family) : null; const SpecificComponent = specific?.AccountFooter; + return SpecificComponent ? ( ) : ( @@ -73,4 +76,5 @@ const AccountFooter = ({ account, parentAccount, status }: Props) => { ); }; + export default AccountFooter; diff --git a/apps/ledger-live-desktop/src/renderer/modals/Send/Body.tsx b/apps/ledger-live-desktop/src/renderer/modals/Send/Body.tsx index a4c87ae3d554..598c18981dc4 100644 --- a/apps/ledger-live-desktop/src/renderer/modals/Send/Body.tsx +++ b/apps/ledger-live-desktop/src/renderer/modals/Send/Body.tsx @@ -344,7 +344,11 @@ const Body = ({ onChangeNFT: handleChangeNFT, shouldSkipAmount, }; - if (!status) return null; + + if (!status) { + return null; + } + return ( {stepId === "confirmation" ? null : } @@ -352,6 +356,7 @@ const Body = ({ ); }; + const m = compose( connect(mapStateToProps, mapDispatchToProps), withTranslation(), diff --git a/apps/ledger-live-desktop/src/renderer/modals/Send/SendAmountFields.tsx b/apps/ledger-live-desktop/src/renderer/modals/Send/SendAmountFields.tsx index 7563272965d0..2819c3c27ab0 100644 --- a/apps/ledger-live-desktop/src/renderer/modals/Send/SendAmountFields.tsx +++ b/apps/ledger-live-desktop/src/renderer/modals/Send/SendAmountFields.tsx @@ -24,4 +24,5 @@ const AmountRelatedField = (props: Props) => { const Cmp = module.component; return ; }; + export default AmountRelatedField; diff --git a/apps/ledger-live-desktop/src/renderer/modals/Send/fields/RecipientField.react.test.tsx b/apps/ledger-live-desktop/src/renderer/modals/Send/fields/RecipientField.react.test.tsx index e5f5d9afed1f..328e0fb4ee62 100644 --- a/apps/ledger-live-desktop/src/renderer/modals/Send/fields/RecipientField.react.test.tsx +++ b/apps/ledger-live-desktop/src/renderer/modals/Send/fields/RecipientField.react.test.tsx @@ -125,16 +125,15 @@ const baseMockTransaction: Transaction = { recipient: "", useAllAmount: false, mode: "send", - family: "ethereum", - gasPrice: null, + family: "evm", + gasPrice: undefined, maxFeePerGas: new BigNumber("28026227316"), maxPriorityFeePerGas: new BigNumber("1000000000"), - userGasLimit: null, - estimatedGasLimit: null, - feeCustomUnit: null, - networkInfo: { - family: "ethereum", - }, + customGasLimit: undefined, + nonce: 0, + gasLimit: new BigNumber("21000"), + chainId: 1, + type: 2, }; const baseMockStatus: TransactionStatus = { diff --git a/apps/ledger-live-desktop/src/renderer/modals/Send/steps/StepAmount.tsx b/apps/ledger-live-desktop/src/renderer/modals/Send/steps/StepAmount.tsx index 10715f627b6f..4c67e681f195 100644 --- a/apps/ledger-live-desktop/src/renderer/modals/Send/steps/StepAmount.tsx +++ b/apps/ledger-live-desktop/src/renderer/modals/Send/steps/StepAmount.tsx @@ -60,6 +60,7 @@ const StepAmount = (props: StepProps) => { }, [specific.nft, transaction]); if (!status) return null; + return ( { maxPriorityFee: maxPriorityFeeError, maxFee: maxFeeError, } = errors; + return ( <> {!isNFTSend ? ( @@ -157,4 +159,5 @@ export class StepAmountFooter extends PureComponent { ); } } + export default StepAmount; diff --git a/apps/ledger-live-desktop/src/renderer/modals/Send/steps/StepConfirmation.tsx b/apps/ledger-live-desktop/src/renderer/modals/Send/steps/StepConfirmation.tsx index 692a018845fd..c649652fa28c 100644 --- a/apps/ledger-live-desktop/src/renderer/modals/Send/steps/StepConfirmation.tsx +++ b/apps/ledger-live-desktop/src/renderer/modals/Send/steps/StepConfirmation.tsx @@ -1,8 +1,9 @@ +// FIXME: to update when implementing edit transaction on evm + import React from "react"; import { Trans } from "react-i18next"; import styled from "styled-components"; import { SyncOneAccountOnMount } from "@ledgerhq/live-common/bridge/react/index"; -import { getMainAccount } from "@ledgerhq/live-common/account/index"; import TrackPage from "~/renderer/analytics/TrackPage"; import { multiline } from "~/renderer/styles/helpers"; import Box from "~/renderer/components/Box"; @@ -14,7 +15,6 @@ import BroadcastErrorDisclaimer from "~/renderer/components/BroadcastErrorDiscla import { OperationDetails } from "~/renderer/drawers/OperationDetails"; import { setDrawer } from "~/renderer/drawers/Provider"; import { StepProps } from "../types"; -import { TransactionHasBeenValidatedError } from "@ledgerhq/errors"; const Container = styled(Box).attrs(() => ({ alignItems: "center", @@ -34,8 +34,6 @@ function StepConfirmation({ isNFTSend, signed, currencyName, - account, - parentAccount, }: StepProps) { if (optimisticOperation) { return ( @@ -59,13 +57,14 @@ function StepConfirmation({ ); } if (error) { - // Edit ethereum transaction nonce error because transaction has been validated - if (error.name === "LedgerAPI4xx" && error.message.includes("nonce too low")) { - const mainAccount = account ? getMainAccount(account, parentAccount) : null; - if (mainAccount?.currency?.family === "ethereum") { - error = new TransactionHasBeenValidatedError(); - } - } + // // Edit ethereum transaction nonce error because transaction has been validated + // if (error.name === "LedgerAPI4xx" && error.message.includes("nonce too low")) { + // const mainAccount = account ? getMainAccount(account, parentAccount) : null; + // if (mainAccount?.currency?.family === "evm") { + // error = new TransactionHasBeenValidatedError(); + // } + // } + return ( { - render() { - const { account, parentAccount, transaction, status, currencyName, isNFTSend } = this.props; - if (!account) return null; - const mainAccount = getMainAccount(account, parentAccount); - if (!mainAccount || !transaction) return null; - const { estimatedFees, amount, totalSpent, warnings } = status; - const txInputs = "txInputs" in status ? status.txInputs : undefined; - const feeTooHigh = warnings.feeTooHigh; - const currency = getAccountCurrency(account); - const feesCurrency = getFeesCurrency(mainAccount); - const feesUnit = getFeesUnit(feesCurrency); - const unit = getAccountUnit(account); - const utxoLag = txInputs ? txInputs.length >= WARN_FROM_UTXO_COUNT : null; - const hasNonEmptySubAccounts = - account.type === "Account" && - (account.subAccounts || []).some(subAccount => subAccount.balance.gt(0)); - const specific = currency ? getLLDCoinFamily(mainAccount.currency.family) : null; - const SpecificSummaryNetworkFeesRow = specific?.StepSummaryNetworkFeesRow; +const StepSummary = (props: StepProps) => { + const { account, parentAccount, transaction, status, currencyName, isNFTSend } = props; - const memo = "memo" in transaction ? transaction.memo : undefined; - return ( - - - {utxoLag ? ( - - - - ) : null} - {transaction.useAllAmount && hasNonEmptySubAccounts ? ( - - - - ) : null} - - - - - - - - - - - -
- -
- - {getAccountName(account)} - - -
-
-
- - - - - - - - + if (!account) { + return null; + } + + const mainAccount = getMainAccount(account, parentAccount); + + if (!mainAccount || !transaction) { + return null; + } + + const { estimatedFees, amount, totalSpent, warnings } = status; + const txInputs = "txInputs" in status ? status.txInputs : undefined; + const { feeTooHigh } = warnings; + const currency = getAccountCurrency(account); + const feesCurrency = getFeesCurrency(mainAccount); + const feesUnit = getFeesUnit(feesCurrency); + const unit = getAccountUnit(account); + const utxoLag = txInputs ? txInputs.length >= WARN_FROM_UTXO_COUNT : null; + const hasNonEmptySubAccounts = + account.type === "Account" && + (account.subAccounts || []).some(subAccount => subAccount.balance.gt(0)); + + const specific = currency ? getLLDCoinFamily(mainAccount.currency.family) : null; + const SpecificSummaryNetworkFeesRow = specific?.StepSummaryNetworkFeesRow; + + const memo = "memo" in transaction ? transaction.memo : undefined; + + return ( + + + {utxoLag ? ( + + + + ) : null} + {transaction.useAllAmount && hasNonEmptySubAccounts ? ( + + + + ) : null} + + + + + + + + + + + +
+ +
+ + {getAccountName(account)} - {transaction.recipientDomain && ( - - {transaction.recipientDomain.domain} - - )} - - - {transaction.recipient} - - +
- - {memo && ( - + + + + + + - + - - - {memo} + {transaction.recipientDomain && ( + + {transaction.recipientDomain.domain} + + )} + + + {transaction.recipient} - )} - {!isNFTSend ? ( - + + + + {memo && ( + + + + + + + {memo} + + + + )} + {!isNFTSend ? ( + + + + + + + + + + + + ) : ( + + )} + {SpecificSummaryNetworkFeesRow ? ( + + ) : ( + <> + - + - ) : ( - - )} - {SpecificSummaryNetworkFeesRow ? ( - - ) : ( - <> - - - + {feeTooHigh ? ( + + + + - - - - - - - {feeTooHigh ? ( - - - - - - - ) : null} - - )} + ) : null} + + )} - {!totalSpent.eq(amount) ? ( - <> - - - - - + {!totalSpent.eq(amount) ? ( + <> + + + + + - - + + + - - - - - ) : null} -
-
- ); - } -} -export class StepSummaryFooter extends PureComponent { - onNext = async () => { - const { transitionTo } = this.props; +
+ + ) : null} +
+
+ ); +}; + +export const StepSummaryFooter = (props: StepProps) => { + const { account, status, bridgePending, transitionTo } = props; + + const onNext = async () => { transitionTo("device"); }; - render() { - const { account, status, bridgePending } = this.props; - if (!account) return null; - const { errors } = status; - const canNext = !bridgePending && !Object.keys(errors).length; - return ( - <> - - - ); + if (!account) { + return null; } -} + + const { errors } = status; + const canNext = !bridgePending && !Object.keys(errors).length; + + return ( + <> + + + ); +}; + +export default StepSummary; diff --git a/apps/ledger-live-desktop/src/renderer/modals/SignMessage/steps/StepSummary.tsx b/apps/ledger-live-desktop/src/renderer/modals/SignMessage/steps/StepSummary.tsx index fbce5848481a..6e6c251f4110 100644 --- a/apps/ledger-live-desktop/src/renderer/modals/SignMessage/steps/StepSummary.tsx +++ b/apps/ledger-live-desktop/src/renderer/modals/SignMessage/steps/StepSummary.tsx @@ -1,16 +1,16 @@ +import { getMainAccount } from "@ledgerhq/live-common/account/index"; +import type { MessageProperties } from "@ledgerhq/types-live"; import React, { memo, useEffect, useState } from "react"; -import styled from "styled-components"; import { Trans, useTranslation } from "react-i18next"; -import { getMainAccount } from "@ledgerhq/live-common/account/index"; -import CryptoCurrencyIcon from "~/renderer/components/CryptoCurrencyIcon"; +import styled from "styled-components"; +import Box from "~/renderer/components/Box"; import Button from "~/renderer/components/Button"; -import { rgba } from "~/renderer/styles/helpers"; -import IconWallet from "~/renderer/icons/Wallet"; +import CryptoCurrencyIcon from "~/renderer/components/CryptoCurrencyIcon"; import Text from "~/renderer/components/Text"; -import Box from "~/renderer/components/Box"; -import type { MessageProperties } from "~/renderer/families/types"; -import { StepProps } from "../types"; import { getLLDCoinFamily } from "~/renderer/families"; +import IconWallet from "~/renderer/icons/Wallet"; +import { rgba } from "~/renderer/styles/helpers"; +import { StepProps } from "../types"; const Circle = styled.div` height: 32px; @@ -34,16 +34,19 @@ const Separator = styled.div` const MessageContainer = styled(Box)` white-space: pre; `; + const PropertiesList = styled.ul` list-style: none; margin-bottom: 10px; `; + const ValueWrapper = styled.span` margin-bottom: 6px; overflow-wrap: break-word; word-wrap: break-word; white-space: normal; `; + const AdvancedMessageArea = styled.pre` border: 1px solid ${p => rgba(p.theme.colors.palette.primary.main, 0.1)}; background-color: ${p => p.theme.colors.palette.background.default}; @@ -80,6 +83,7 @@ const MessageProperty = memo(({ label, value }: MessageProperties[0]) => {
); }); + MessageProperty.displayName = "MessageProperty"; const MessagePropertiesComp = memo((props: { properties: MessageProperties | null }) => { @@ -103,7 +107,7 @@ export default function StepSummary({ account, message: messageData }: StepProps useEffect(() => { if (messageData.standard === "EIP712") { const specific = getLLDCoinFamily(mainAccount.currency.family); - specific?.message?.getMessageProperties(mainAccount, messageData).then(setMessageFields); + specific?.message?.getMessageProperties(messageData).then(setMessageFields); } }, [account.currency.family, mainAccount, messageData, setMessageFields]); diff --git a/apps/ledger-live-desktop/src/renderer/modals/SignTransaction/steps/StepSummary.tsx b/apps/ledger-live-desktop/src/renderer/modals/SignTransaction/steps/StepSummary.tsx index d470608767c9..adbcfe864440 100644 --- a/apps/ledger-live-desktop/src/renderer/modals/SignTransaction/steps/StepSummary.tsx +++ b/apps/ledger-live-desktop/src/renderer/modals/SignTransaction/steps/StepSummary.tsx @@ -68,7 +68,7 @@ const StepSummary = (props: StepProps) => { return null; } - const { estimatedFees, amount, totalSpent, warnings } = status; + const { estimatedFees, amount, totalSpent, errors, warnings } = status; const txInputs = "txInputs" in status ? status.txInputs : undefined; const feeTooHigh = warnings.feeTooHigh; const currency = getAccountCurrency(account); @@ -103,6 +103,13 @@ const StepSummary = (props: StepProps) => { />
) : null} + {/* Since a sign transaction (live-app related) can start at the summary step + (if fees provided by live-app), we need to display transaction status errors here */} + {errors && Object.keys(errors).length ? ( + + (errors)[0]} /> + + ) : null} diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/FormSelectors/FormInputs.tsx b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/FormSelectors/FormInputs.tsx index ddddbf16fe71..42e11d265453 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/FormSelectors/FormInputs.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/FormSelectors/FormInputs.tsx @@ -33,6 +33,7 @@ type FormInputsProps = { isSendMaxLoading: boolean; updateSelectedRate: SwapDataType["updateSelectedRate"]; }; + type SwapButtonProps = { onClick: SwapTransactionType["reverseSwap"]; disabled: boolean; @@ -43,6 +44,7 @@ const RoundButton = styled(Button)` border-radius: 9999px; height: initial; `; + const Main = styled.section` display: flex; flex-direction: column; diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/FormSelectors/FromRow.tsx b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/FormSelectors/FromRow.tsx index aedc9fa01c28..f07565ad6bbf 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/FormSelectors/FromRow.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/FormSelectors/FromRow.tsx @@ -51,6 +51,20 @@ const SwapStatusText = styled(Text)( `, ); +/* @dev: Yeah, Im sorry if you read this, design asked us to + override the input component when it is called from the swap form. */ +const InputSection = styled(Box)` + & ${ErrorContainer} { + font-weight: 500; + font-size: 11px; + text-align: right; + margin-left: calc(calc(100% + 30px) * -1); + margin-top: 6px; + align-self: flex-end; + margin-right: -15px; + } +`; + // Pick a default source account if none are selected. // TODO use live-common once its ready const usePickDefaultAccount = ( @@ -96,20 +110,6 @@ type Props = { updateSelectedRate: SwapDataType["updateSelectedRate"]; }; -/* @dev: Yeah, Im sorry if you read this, design asked us to - override the input component when it is called from the swap form. */ -const InputSection = styled(Box)` - & ${ErrorContainer} { - font-weight: 500; - font-size: 11px; - text-align: right; - margin-left: calc(calc(100% + 30px) * -1); - margin-top: 6px; - align-self: flex-end; - margin-right: -15px; - } -`; - function FromRow({ fromAmount, setFromAmount, @@ -127,12 +127,15 @@ function FromRow({ const unit = fromAccount && getAccountUnit(fromAccount); const { t } = useTranslation(); usePickDefaultAccount(accounts, fromAccount, setFromAccount); - const trackEditAccount = () => + + const trackEditAccount = () => { track("button_clicked", { button: "Edit source account", page: "Page Swap Form", ...swapDefaultTrack, }); + }; + const setAccountAndTrack = (account: AccountLike) => { updateSelectedRate(); const name = account ? getAccountName(account) : undefined; @@ -144,6 +147,7 @@ function FromRow({ }); setFromAccount(account); }; + const setValue = (fromAmount: BigNumber) => { track("button_clicked", { button: "Amount input", @@ -154,6 +158,7 @@ function FromRow({ updateSelectedRate(); setFromAmount(fromAmount); }; + const toggleMaxAndTrack = (state: unknown) => { track("button_clicked", { button: "max", @@ -231,4 +236,5 @@ function FromRow({ ); } + export default React.memo(FromRow); diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/index.tsx b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/index.tsx index 30be4a4200de..df4ff418025f 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/index.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/index.tsx @@ -52,9 +52,11 @@ const Wrapper = styled(Box).attrs({ row-gap: 2rem; max-width: 37rem; `; + const Hide = styled.div` opacity: 0; `; + const idleTime = 60 * 60000; // 1 hour const Button = styled(ButtonBase)` @@ -69,10 +71,12 @@ export const useProviders = () => { if (providers) dispatch(updateProvidersAction(providers)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [providers]); + useEffect(() => { if (providersError) dispatch(resetSwapAction()); // eslint-disable-next-line react-hooks/exhaustive-deps }, [providersError]); + return { storedProviders, providers, @@ -217,6 +221,7 @@ const SwapForm = () => { swapTransaction.swap.to.account && swapTransaction.swap.from.amount && swapTransaction.swap.from.amount.gt(0); + const onSubmit = () => { if (!exchangeRate) return; @@ -293,6 +298,7 @@ const SwapForm = () => { } } }; + const sourceAccount = swapTransaction.swap.from.account; const sourceCurrency = swapTransaction.swap.from.currency; const targetCurrency = swapTransaction.swap.to.currency; @@ -311,17 +317,20 @@ const SwapForm = () => { const setFromAccount = (account: AccountLike | undefined) => { swapTransaction.setFromAccount(account); }; + const setFromAmount = (amount: BigNumber) => { swapTransaction.setFromAmount(amount); }; + const setToCurrency = (currency: TokenCurrency | CryptoCurrency | undefined) => { swapTransaction.setToCurrency(currency); }; + const toggleMax = () => { swapTransaction.toggleMax(); }; - if (storedProviders?.length) + if (storedProviders?.length) { return ( @@ -372,6 +381,7 @@ const SwapForm = () => { ); + } // TODO: ensure that the error is catch by Sentry in this case if (storedProviders?.length === 0 || providersError) { diff --git a/apps/ledger-live-desktop/src/renderer/screens/lend/modals/Enable/types.ts b/apps/ledger-live-desktop/src/renderer/screens/lend/modals/Enable/types.ts deleted file mode 100644 index 6b0607a655ce..000000000000 --- a/apps/ledger-live-desktop/src/renderer/screens/lend/modals/Enable/types.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { TFunction } from "i18next"; -import { Device } from "@ledgerhq/live-common/hw/actions/types"; -import { Step } from "~/renderer/components/Stepper"; -import { Account, AccountLike, Operation } from "@ledgerhq/types-live"; -// eslint-disable-next-line no-restricted-imports -import { Transaction, TransactionStatus } from "@ledgerhq/live-common/families/ethereum/types"; -export type StepId = "amount" | "connectDevice" | "confirmation"; -export type StepProps = { - t: TFunction; - transitionTo: (a: string) => void; - device: Device | undefined | null; - account: AccountLike; - parentAccount: Account; - onRetry: (a: void) => void; - onClose: () => void; - openModal: (key: string, config?: unknown) => void; - optimisticOperation: Operation | undefined; - bridgeError: Error | undefined | null; - transactionError: Error | undefined | null; - signed: boolean; - transaction: Transaction | undefined | null; - status: TransactionStatus; - onChangeTransaction: (a: Transaction) => void; - onTransactionError: (a: Error) => void; - onOperationBroadcasted: (a: Operation) => void; - setSigned: (a: boolean) => void; - bridgePending: boolean; - onUpdateTransaction: (updater: (_: Transaction) => void) => void; -}; -export type St = Step; diff --git a/apps/ledger-live-desktop/src/renderer/screens/lend/modals/Supply/types.ts b/apps/ledger-live-desktop/src/renderer/screens/lend/modals/Supply/types.ts deleted file mode 100644 index 141f265a6862..000000000000 --- a/apps/ledger-live-desktop/src/renderer/screens/lend/modals/Supply/types.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { TFunction } from "i18next"; -import { Device } from "@ledgerhq/live-common/hw/actions/types"; -import { Step } from "~/renderer/components/Stepper"; -import { Account, AccountLike, TokenAccount, Operation } from "@ledgerhq/types-live"; -import { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets"; -// eslint-disable-next-line no-restricted-imports -import { Transaction, TransactionStatus } from "@ledgerhq/live-common/families/ethereum/types"; -export type StepId = "amount" | "connectDevice" | "confirmation"; -export type StepProps = { - t: TFunction; - transitionTo: (a: string) => void; - device: Device | undefined | null; - account: TokenAccount | undefined | null; - accounts: AccountLike[] | undefined | null; - currency: CryptoCurrency | TokenCurrency; - parentAccount: Account | undefined | null; - onRetry: (a: void) => void; - onClose: () => void; - openModal: (key: string, config?: unknown) => void; - onChangeAccount: (nextAccount: AccountLike, nextParentAccount?: Account) => void; - optimisticOperation: Operation | undefined; - bridgeError: Error | undefined | null; - transactionError: Error | undefined | null; - signed: boolean; - transaction: Transaction | undefined | null; - status: TransactionStatus; - onChangeTransaction: (a: Transaction) => void; - onUpdateTransaction: (updater: (a: Transaction) => void) => void; - onTransactionError: (a: Error) => void; - onOperationBroadcasted: (a: Operation) => void; - setSigned: (a: boolean) => void; - bridgePending: boolean; -}; -export type St = Step; diff --git a/apps/ledger-live-desktop/src/renderer/screens/lend/modals/Withdraw/types.ts b/apps/ledger-live-desktop/src/renderer/screens/lend/modals/Withdraw/types.ts deleted file mode 100644 index 318bc4341667..000000000000 --- a/apps/ledger-live-desktop/src/renderer/screens/lend/modals/Withdraw/types.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { TFunction } from "i18next"; -import { Device } from "@ledgerhq/live-common/hw/actions/types"; -import { Step } from "~/renderer/components/Stepper"; -import { Account, TokenAccount, Operation } from "@ledgerhq/types-live"; -// eslint-disable-next-line no-restricted-imports -import { Transaction, TransactionStatus } from "@ledgerhq/live-common/families/ethereum/types"; -export type StepId = "amount" | "connectDevice" | "confirmation"; -export type StepProps = { - t: TFunction; - transitionTo: (a: string) => void; - device: Device | undefined | null; - account: TokenAccount; - parentAccount: Account; - onRetry: (a: void) => void; - onClose: () => void; - openModal: (key: string, config?: unknown) => void; - optimisticOperation: Operation | undefined; - bridgeError: Error | undefined | null; - transactionError: Error | undefined | null; - signed: boolean; - transaction: Transaction | undefined | null; - status: TransactionStatus; - onChangeTransaction: (a: Transaction) => void; - onTransactionError: (a: Error) => void; - onOperationBroadcasted: (a: Operation) => void; - setSigned: (a: boolean) => void; - bridgePending: boolean; - onUpdateTransaction: (updater: (_: Transaction) => void) => void; -}; -export type St = Step; diff --git a/apps/ledger-live-desktop/tests/specs/services/liveapp-sdk.spec.ts b/apps/ledger-live-desktop/tests/specs/services/liveapp-sdk.spec.ts index 6371a00b75ed..d44f0f92d93a 100644 --- a/apps/ledger-live-desktop/tests/specs/services/liveapp-sdk.spec.ts +++ b/apps/ledger-live-desktop/tests/specs/services/liveapp-sdk.spec.ts @@ -122,33 +122,41 @@ test("Live App SDK methods @smoke", async ({ page }) => { * END OF SIGN BITCOIN TRANSACTION TESTS */ + /** + * This test is flaky, so disabling for now. + * Furthermore, this is testing deprecated features (live-app-sdk and legacy ethereum tx) + * This whole testsuite should be reworked to be more consistent with actual behaviour and to test wallet-api + * Also, what are we actually testing here and are we using the right tools to do so? + * If we want to test the interaction with live-app-sdk (or wallet-api) it should rather be tested as parto of + * the libs/ledger-live-common/src/platform logic (or libs/ledger-live-common/src/wallet-api respecitvely) + * If we want to test the flow logic and / or UI consistency, it could be tested using React Testing Library + * (https://testing-library.com/docs/react-testing-library/intro/) and / or jest snapshot testing (https://jestjs.io/docs/snapshot-testing) + */ /** * START OF SIGN ETHEREUM TRANSACTION TESTS */ - - await test.step("Sign ethereum Transaction - info modal", async () => { - await liveAppWebview.signEthereumTransaction(); - await expect.soft(page).toHaveScreenshot("live-app-sign-ethereum-transaction-info.png", { - timeout: 20000, - }); - }); - - await test.step("Sign ethereum Transaction - confirmation modal", async () => { - await modal.continueToSignTransaction(); - await layout.waitForLoadingSpinnerToHaveDisappeared(); - await modal.waitForConfirmationScreenToBeDisplayed(); - await expect(page.locator("text=0.00000000000000123")).toBeVisible(); - // This screenshot is flaky as the loading spinner appears again after this confirm modal, and on slow CI runners the screenshot can take a picture of this instead of the confirm. - // await expect.soft(page).toHaveScreenshot("live-app-sign-transaction-confirm.png"); - }); - - await test.step("Sign ethereum Transaction - signature output", async () => { - await modal.waitForModalToDisappear(); - await liveAppWebview.waitForCorrectTextInWebview( - "mock_op_100_mock:1:ethereum:true_ethereum_0:", - ); - }); - + // await test.step("Sign ethereum Transaction - info modal", async () => { + // await liveAppWebview.signEthereumTransaction(); + // await expect.soft(page).toHaveScreenshot("live-app-sign-ethereum-transaction-info.png", { + // timeout: 20000, + // }); + // }); + + // await test.step("Sign ethereum Transaction - confirmation modal", async () => { + // await modal.continueToSignTransaction(); + // await layout.waitForLoadingSpinnerToHaveDisappeared(); + // await modal.waitForConfirmationScreenToBeDisplayed(); + // await expect(page.locator("text=0.00000000000000123")).toBeVisible(); + // // This screenshot is flaky as the loading spinner appears again after this confirm modal, and on slow CI runners the screenshot can take a picture of this instead of the confirm. + // // await expect.soft(page).toHaveScreenshot("live-app-sign-transaction-confirm.png"); + // }); + + // await test.step("Sign ethereum Transaction - signature output", async () => { + // await modal.waitForModalToDisappear(); + // await liveAppWebview.waitForCorrectTextInWebview( + // "mock_op_100_mock:1:ethereum:true_ethereum_0:", + // ); + // }); /** * END OF SIGN ETHEREUM TRANSACTION TESTS */ diff --git a/apps/ledger-live-mobile/e2e/specs/swap/dexSwap.spec.ts b/apps/ledger-live-mobile/e2e/specs/swap/dexSwap.spec.ts index 26276397ab56..d4c07a043b51 100644 --- a/apps/ledger-live-mobile/e2e/specs/swap/dexSwap.spec.ts +++ b/apps/ledger-live-mobile/e2e/specs/swap/dexSwap.spec.ts @@ -41,11 +41,22 @@ describe("DEX Swap", () => { const title = await detox.web.element(detox.by.web.id("__next")).getTitle(); expect(title).toBe("Ledger Platform Apps"); - // Full url: "https://dapp-browser.apps.ledger.com/?params=%7B%22dappUrl%22%3A%22https%3A%2F%2Fapp.1inch.io%2F%23%2F1%2Fsimple%2Fswap%2Feth%2Fusdt%3FledgerLive%3Dtrue%26sourceTokenAmount%3D11309838568372696785%22%2C%22nanoApp%22%3A%221inch%22%2C%22dappName%22%3A%221inch%22%2C%22networks%22%3A%5B%7B%22currency%22%3A%22ethereum%22%2C%22chainID%22%3A1%2C%22nodeURL%22%3A%22wss%3A%2F%2Feth-mainnet.ws.alchemyapi.io%2Fv2%2F0fyudoTG94QWC0tEtfJViM9v2ZXJuij2%22%7D%2C%7B%22currency%22%3A%22bsc%22%2C%22chainID%22%3A56%2C%22nodeURL%22%3A%22https%3A%2F%2Fbsc-dataseed.binance.org%2F%22%7D%2C%7B%22currency%22%3A%22polygon%22%2C%22chainID%22%3A137%2C%22nodeURL%22%3A%22https%3A%2F%2Fpolygon-mainnet.g.alchemy.com%2Fv2%2FoPIxZM7kXsPVVY1Sk0kOQwkoIOpSu8PE%22%7D%5D%7D&theme=light&lang=en&name=1inch&accountId=mock%3A1%3Aethereum%3Atrue_ethereum_1%3A", + // Full url: "https://dapp-browser.apps.ledger.com/?params=%7B%22dappUrl%22%3A%22https%3A%2F%2Fapp.1inch.io%2F%23%2F1%2Fsimple%2Fswap%2Feth%2Fusdt%3FledgerLive%3Dtrue%26sourceTokenAmount%3D11310048568372696785%22%2C%22nanoApp%22%3A%221inch%22%2C%22dappName%22%3A%221inch%22%2C%22networks%22%3A%5B%7B%22currency%22%3A%22ethereum%22%2C%22chainID%22%3A1%2C%22nodeURL%22%3A%22wss%3A%2F%2Feth-mainnet.ws.alchemyapi.io%2Fv2%2F0fyudoTG94QWC0tEtfJViM9v2ZXJuij2%22%7D%2C%7B%22currency%22%3A%22bsc%22%2C%22chainID%22%3A56%2C%22nodeURL%22%3A%22https%3A%2F%2Fbsc-dataseed.binance.org%2F%22%7D%2C%7B%22currency%22%3A%22polygon%22%2C%22chainID%22%3A137%2C%22nodeURL%22%3A%22https%3A%2F%2Fpolygon-mainnet.g.alchemy.com%2Fv2%2FoPIxZM7kXsPVVY1Sk0kOQwkoIOpSu8PE%22%7D%5D%7D&theme=light&lang=en&name=1inch&accountId=mock%3A1%3Aethereum%3Atrue_ethereum_1%3A" const url = await detox.web.element(detox.by.web.id("__next")).getCurrentUrl(); expect(url).toContain("app.1inch.io"); expect(url).toContain("usdt"); - expect(url).toContain("sourceTokenAmount%3D11309838568372696785"); + /** + * Not sure if it makes sence to test the exact amount as it can be dynamic + * (depending on network fees) and not specifically relevant for this test. + * It might be more relevent in a unit test related to the swap logic. + * Here it could make more sense to test the presence of a `sourceTokenAmount` + * query param. + * For example: + * `expect(url).toContain("sourceTokenAmount%3D");` + * or (to test that an amount is provided to the query param, without the need for the exact value): + * `expect(url).toContain("sourceTokenAmount%3D11");` + */ + expect(url).toContain("sourceTokenAmount%3D11310048568372696785"); expect(url).toContain("currency%22%3A%22ethereum"); expect(url).toContain("accountId=mock%3A1%3Aethereum"); diff --git a/apps/ledger-live-mobile/package.json b/apps/ledger-live-mobile/package.json index e9af9d6925bc..070f19cb8efb 100644 --- a/apps/ledger-live-mobile/package.json +++ b/apps/ledger-live-mobile/package.json @@ -67,7 +67,6 @@ "@ledgerhq/devices": "workspace:^", "@ledgerhq/domain-service": "workspace:^", "@ledgerhq/errors": "workspace:^", - "@ledgerhq/evm-tools": "workspace:^", "@ledgerhq/hw-transport": "workspace:^", "@ledgerhq/hw-transport-http": "workspace:^", "@ledgerhq/live-common": "workspace:^", diff --git a/apps/ledger-live-mobile/src/components/EditOperationCard.tsx b/apps/ledger-live-mobile/src/components/EditOperationCard.tsx index 6f58936aa0ee..483e3e677141 100644 --- a/apps/ledger-live-mobile/src/components/EditOperationCard.tsx +++ b/apps/ledger-live-mobile/src/components/EditOperationCard.tsx @@ -1,12 +1,14 @@ -import React, { useCallback } from "react"; +// FIXME: to update when implementing edit transaction on evm + +import React from "react"; import { useTranslation } from "react-i18next"; import { SideImageCard } from "@ledgerhq/native-ui"; import { Account, AccountLike, Operation } from "@ledgerhq/types-live"; import { useFeature } from "@ledgerhq/live-common/featureFlags/index"; -import { useNavigation } from "@react-navigation/core"; +// import { useNavigation } from "@react-navigation/core"; import SectionContainer from "../screens/WalletCentricSections/SectionContainer"; -import { NavigatorName, ScreenName } from "../const"; +// import { NavigatorName, ScreenName } from "../const"; type EditOperationCardProps = { oldestEditableOperation: Operation; @@ -20,25 +22,29 @@ export const EditOperationCard = ({ oldestEditableOperation, isOperationStuck, onPress, + // eslint-disable-next-line @typescript-eslint/no-unused-vars account, + // eslint-disable-next-line @typescript-eslint/no-unused-vars parentAccount, }: EditOperationCardProps) => { const { t } = useTranslation(); const flag = useFeature("editEthTx"); - const navigation = useNavigation(); + // const navigation = useNavigation(); + + // const onEditTrnasctionCardPress = useCallback(() => { + // if (account) { + // navigation.navigate(NavigatorName.EditTransaction, { + // screen: ScreenName.EditTransactionMethodSelection, + // params: { + // operation: oldestEditableOperation, + // account, + // parentAccount, + // }, + // }); + // } + // }, [account, oldestEditableOperation, parentAccount, navigation]); - const onEditTrnasctionCardPress = useCallback(() => { - if (account) { - navigation.navigate(NavigatorName.EditTransaction, { - screen: ScreenName.EditTransactionMethodSelection, - params: { - operation: oldestEditableOperation, - account, - parentAccount, - }, - }); - } - }, [account, oldestEditableOperation, parentAccount, navigation]); + const onEditTrnasctionCardPress = () => {}; return flag?.enabled ? ( diff --git a/apps/ledger-live-mobile/src/components/Nft/NftViewer.tsx b/apps/ledger-live-mobile/src/components/Nft/NftViewer.tsx index ab485f8e96f2..975752e7dfd3 100644 --- a/apps/ledger-live-mobile/src/components/Nft/NftViewer.tsx +++ b/apps/ledger-live-mobile/src/components/Nft/NftViewer.tsx @@ -68,6 +68,7 @@ import NftViewerBackground from "./NftViewerBackground"; import NftViewerScreenHeader from "./NftViewerScreenHeader"; import invariant from "invariant"; import DiscreetModeContext, { withDiscreetMode } from "../../context/DiscreetModeContext"; +import { EvmNftTransaction, Transaction } from "@ledgerhq/coin-evm/types/index"; type Props = CompositeScreenProps< | StackNavigatorProps @@ -209,29 +210,19 @@ const NftViewer = ({ route }: Props) => { }; const goToRecipientSelection = useCallback(() => { - const bridge = getAccountBridge(account); + const bridge = getAccountBridge(account); const defaultTransaction = bridge.createTransaction(account); - let transaction; - if (defaultTransaction.family === "evm") { - transaction = bridge.updateTransaction(defaultTransaction, { - mode: nft?.standard?.toLowerCase(), - nft: { - tokenId: nft?.tokenId, - // Quantity is set to Infinity first to allow the user to change it on the amount page - quantity: new BigNumber(nftCapabilities.hasQuantity ? Infinity : 1), - contract: nft?.contract, - }, - }); - } else if (defaultTransaction.family === "ethereum") { - transaction = bridge.updateTransaction(defaultTransaction, { - tokenIds: [nft?.tokenId], + const transaction = bridge.updateTransaction(defaultTransaction, { + mode: nft.standard.toLowerCase() as EvmNftTransaction["mode"], + nft: { + tokenId: nft.tokenId, // Quantity is set to Infinity first to allow the user to change it on the amount page - quantities: [new BigNumber(nftCapabilities.hasQuantity ? Infinity : 1)], - collection: nft?.contract, - mode: `${nft?.standard?.toLowerCase()}.transfer`, - }); - } + quantity: new BigNumber(nftCapabilities.hasQuantity ? Infinity : 1), + contract: nft.contract, + collectionName: collectionMetadata?.tokenName ?? "", + }, + }); track("button_clicked", { button: "Send NFT", @@ -244,7 +235,7 @@ const NftViewer = ({ route }: Props) => { transaction, }, }); - }, [account, nft, nftCapabilities.hasQuantity, navigation]); + }, [account, nft, nftCapabilities.hasQuantity, navigation, collectionMetadata]); const properties = useMemo(() => { if (isLoading && !nftMetadata?.properties?.length) { diff --git a/apps/ledger-live-mobile/src/components/OperationRow/index.tsx b/apps/ledger-live-mobile/src/components/OperationRow/index.tsx index 8068ef4c1030..9383c045542a 100644 --- a/apps/ledger-live-mobile/src/components/OperationRow/index.tsx +++ b/apps/ledger-live-mobile/src/components/OperationRow/index.tsx @@ -18,7 +18,6 @@ import { isEqual } from "lodash"; import { useSelector } from "react-redux"; import { getEnv } from "@ledgerhq/live-env"; import { isEditableOperation } from "@ledgerhq/coin-framework/operation"; - import CurrencyUnitValue from "../CurrencyUnitValue"; import CounterValue from "../CounterValue"; import OperationIcon from "../OperationIcon"; diff --git a/apps/ledger-live-mobile/src/components/RootDrawer/RootDrawer.tsx b/apps/ledger-live-mobile/src/components/RootDrawer/RootDrawer.tsx index 44104c48fa21..63c053b31c1a 100644 --- a/apps/ledger-live-mobile/src/components/RootDrawer/RootDrawer.tsx +++ b/apps/ledger-live-mobile/src/components/RootDrawer/RootDrawer.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; -import { EthereumStakingDrawer } from "../../families/ethereum/EthereumStakingDrawer"; +import { EvmStakingDrawer } from "../../families/evm/StakingDrawer"; import { RootDrawerProvider, useRootDrawerContext } from "../../context/RootDrawerContext"; import { InitialDrawerID, RootDrawerProps } from "./types"; @@ -32,8 +32,8 @@ export async function getInitialDrawersToShow(initialDrawers: InitialDrawerID[]) export function RootDrawerSelector() { const { drawer } = useRootDrawerContext(); switch (drawer.id) { - case "EthStakingDrawer": - return ; + case "EvmStakingDrawer": + return ; case InitialDrawerID.PTXServicesAppleDrawerKey: return ; default: diff --git a/apps/ledger-live-mobile/src/components/RootNavigator/BaseNavigator.tsx b/apps/ledger-live-mobile/src/components/RootNavigator/BaseNavigator.tsx index dfb0b3564ebd..20efaaf0dbec 100644 --- a/apps/ledger-live-mobile/src/components/RootNavigator/BaseNavigator.tsx +++ b/apps/ledger-live-mobile/src/components/RootNavigator/BaseNavigator.tsx @@ -1,3 +1,5 @@ +// FIXME: to update when implementing edit transaction on evm + import React, { useMemo } from "react"; import { createStackNavigator, @@ -83,7 +85,8 @@ import { NavigationHeaderCloseButtonAdvanced, } from "../NavigationHeaderCloseButton"; import { RootDrawer } from "../RootDrawer/RootDrawer"; -import EditTransactionNavigator from "../../families/ethereum/EditTransactionFlow/EditTransactionNavigator"; +// to keep until edit transaction on evm is implemented +// import EditTransactionNavigator from "../../families/ethereum/EditTransactionFlow/EditTransactionNavigator"; import { DrawerProps } from "../RootDrawer/types"; const Stack = createStackNavigator(); @@ -534,11 +537,12 @@ export default function BaseNavigator() { headerLeft: () => null, }} /> - + /> */} ); diff --git a/apps/ledger-live-mobile/src/components/RootNavigator/types/BaseNavigator.ts b/apps/ledger-live-mobile/src/components/RootNavigator/types/BaseNavigator.ts index eb11272d1db2..2f574b9c4313 100644 --- a/apps/ledger-live-mobile/src/components/RootNavigator/types/BaseNavigator.ts +++ b/apps/ledger-live-mobile/src/components/RootNavigator/types/BaseNavigator.ts @@ -1,3 +1,5 @@ +// FIXME: to update when implementing edit transaction on evm + import type { Operation, AccountLike, Account, DeviceInfo } from "@ledgerhq/types-live"; import type { NavigatorScreenParams, ParamListBase } from "@react-navigation/native"; import type { RampCatalogEntry } from "@ledgerhq/live-common/platform/providers/RampCatalogProvider/types"; @@ -58,7 +60,7 @@ import type { CosmosClaimRewardsFlowParamList } from "../../../families/cosmos/C import type { SolanaDelegationFlowParamList } from "../../../families/solana/DelegationFlow/types"; import type { StellarAddAssetFlowParamList } from "../../../families/stellar/AddAssetFlow/types"; import type { TezosDelegationFlowParamList } from "../../../families/tezos/DelegationFlow/types"; -import type { EditTransactionParamList } from "../../../families/ethereum/EditTransactionFlow/EditTransactionParamList"; +// import type { EditTransactionParamList } from "../../../families/ethereum/EditTransactionFlow/EditTransactionParamList"; import type { TronVoteFlowParamList } from "../../../families/tron/VoteFlow/types"; import type { NoFundsNavigatorParamList } from "./NoFundsNavigator"; import type { StakeNavigatorParamList } from "./StakeNavigator"; @@ -286,7 +288,7 @@ export type BaseNavigatorStackParamList = { [NavigatorName.CosmosClaimRewardsFlow]: NavigatorScreenParams; // Ethereum - [NavigatorName.EditTransaction]: NavigatorScreenParams; + // [NavigatorName.EditTransaction]: NavigatorScreenParams; // Solana [NavigatorName.SolanaDelegationFlow]: NavigatorScreenParams; diff --git a/apps/ledger-live-mobile/src/components/RootNavigator/types/SendFundsNavigator.ts b/apps/ledger-live-mobile/src/components/RootNavigator/types/SendFundsNavigator.ts index adbb2b654c76..fb4e3777728a 100644 --- a/apps/ledger-live-mobile/src/components/RootNavigator/types/SendFundsNavigator.ts +++ b/apps/ledger-live-mobile/src/components/RootNavigator/types/SendFundsNavigator.ts @@ -3,10 +3,6 @@ import { Account, AccountLike, ProtoNFT } from "@ledgerhq/types-live"; import type { Device } from "@ledgerhq/live-common/hw/actions/types"; import type { Operation } from "@ledgerhq/types-live"; import type { Transaction, TransactionStatus } from "@ledgerhq/live-common/generated/types"; -import type { - Transaction as EthereumTransaction, - TransactionRaw as EtherumTransactionRaw, -} from "@ledgerhq/live-common/families/ethereum/types"; import type { Transaction as EvmTransaction } from "@ledgerhq/coin-evm/types/index"; import type { CardanoAccount, @@ -169,36 +165,6 @@ export type SendFundsNavigatorStackParamList = { | ScreenName.SendSelectDevice | ScreenName.SwapForm; }; - [ScreenName.EthereumCustomFees]: { - accountId: string; - parentId?: string; - transaction: EthereumTransaction; - setTransaction: Result["setTransaction"]; - transactionRaw?: EtherumTransactionRaw; - currentNavigation: - | ScreenName.SignTransactionSummary - | ScreenName.SendSummary - | ScreenName.SwapForm; - nextNavigation: - | ScreenName.SignTransactionSelectDevice - | ScreenName.SendSelectDevice - | ScreenName.SwapForm; - }; - [ScreenName.EthereumEditGasLimit]: { - accountId: string; - parentId?: string; - setGasLimit: (_: BigNumber) => void; - gasLimit?: BigNumber | null; - transaction: EthereumTransaction; - currentNavigation: - | ScreenName.SignTransactionSummary - | ScreenName.SendSummary - | ScreenName.SwapForm; - nextNavigation: - | ScreenName.SignTransactionSelectDevice - | ScreenName.SendSelectDevice - | ScreenName.SwapForm; - }; [ScreenName.EvmCustomFees]: { accountId: string; parentId?: string; diff --git a/apps/ledger-live-mobile/src/components/RootNavigator/types/SignTransactionNavigator.ts b/apps/ledger-live-mobile/src/components/RootNavigator/types/SignTransactionNavigator.ts index 55156bbdf210..424eacf09ec5 100644 --- a/apps/ledger-live-mobile/src/components/RootNavigator/types/SignTransactionNavigator.ts +++ b/apps/ledger-live-mobile/src/components/RootNavigator/types/SignTransactionNavigator.ts @@ -1,5 +1,4 @@ import { Transaction, TransactionStatus } from "@ledgerhq/live-common/generated/types"; -import type { Transaction as EthereumTransaction } from "@ledgerhq/live-common/families/ethereum/types"; import type { Transaction as EvmTransaction } from "@ledgerhq/coin-evm/types/index"; import type { CardanoAccount, @@ -125,34 +124,6 @@ export type SignTransactionNavigatorParamList = { | ScreenName.SendSelectDevice | ScreenName.SwapForm; }; - [ScreenName.EthereumCustomFees]: { - accountId: string; - parentId?: string; - transaction: EthereumTransaction; - currentNavigation: - | ScreenName.SignTransactionSummary - | ScreenName.SendSummary - | ScreenName.SwapForm; - nextNavigation: - | ScreenName.SignTransactionSelectDevice - | ScreenName.SendSelectDevice - | ScreenName.SwapForm; - }; - [ScreenName.EthereumEditGasLimit]: { - accountId: string; - parentId?: string; - setGasLimit: (_: BigNumber) => void; - gasLimit?: BigNumber | null; - transaction: EthereumTransaction; - currentNavigation: - | ScreenName.SignTransactionSummary - | ScreenName.SendSummary - | ScreenName.SwapForm; - nextNavigation: - | ScreenName.SignTransactionSelectDevice - | ScreenName.SendSelectDevice - | ScreenName.SwapForm; - }; [ScreenName.EvmCustomFees]: { accountId: string; parentId?: string; diff --git a/apps/ledger-live-mobile/src/components/RootNavigator/types/SwapNavigator.ts b/apps/ledger-live-mobile/src/components/RootNavigator/types/SwapNavigator.ts index 977d3247b1df..c1630987d6a9 100644 --- a/apps/ledger-live-mobile/src/components/RootNavigator/types/SwapNavigator.ts +++ b/apps/ledger-live-mobile/src/components/RootNavigator/types/SwapNavigator.ts @@ -1,8 +1,8 @@ import { ExchangeRate, SwapDataType } from "@ledgerhq/live-common/exchange/swap/types"; import { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets"; import { Transaction } from "@ledgerhq/live-common/generated/types"; -import type { Transaction as EthereumTransaction } from "@ledgerhq/live-common/families/ethereum/types"; import type { Transaction as EvmTransaction } from "@ledgerhq/coin-evm/types/index"; + import type { DetailsSwapParamList, DefaultAccountSwapParamList, @@ -10,7 +10,6 @@ import type { SwapPendingOperation, SwapOperation, } from "../../../screens/Swap/types"; - import type { CardanoAccount, Transaction as CardanoTransaction, @@ -135,34 +134,6 @@ export type SwapNavigatorParamList = { | ScreenName.SendSelectDevice | ScreenName.SwapForm; }; - [ScreenName.EthereumCustomFees]: { - accountId: string; - parentId?: string; - transaction: EthereumTransaction; - currentNavigation: - | ScreenName.SignTransactionSummary - | ScreenName.SendSummary - | ScreenName.SwapForm; - nextNavigation: - | ScreenName.SignTransactionSelectDevice - | ScreenName.SendSelectDevice - | ScreenName.SwapForm; - }; - [ScreenName.EthereumEditGasLimit]: { - accountId: string; - parentId?: string; - setGasLimit: (_: BigNumber) => void; - gasLimit?: BigNumber | null; - transaction: EthereumTransaction; - currentNavigation: - | ScreenName.SignTransactionSummary - | ScreenName.SendSummary - | ScreenName.SwapForm; - nextNavigation: - | ScreenName.SignTransactionSelectDevice - | ScreenName.SendSelectDevice - | ScreenName.SwapForm; - }; [ScreenName.EvmCustomFees]: { accountId: string; parentId?: string; diff --git a/apps/ledger-live-mobile/src/components/Stake/types.ts b/apps/ledger-live-mobile/src/components/Stake/types.ts index 4e05f337ff8b..49d6e8d06ea2 100644 --- a/apps/ledger-live-mobile/src/components/Stake/types.ts +++ b/apps/ledger-live-mobile/src/components/Stake/types.ts @@ -1,4 +1,4 @@ -export type StakingDrawerID = "EthStakingDrawer"; +type StakingDrawerID = "EvmStakingDrawer"; export type StakingDrawerNavigationProps = { id: StakingDrawerID; diff --git a/apps/ledger-live-mobile/src/components/ValidateMessageOnDevice.tsx b/apps/ledger-live-mobile/src/components/ValidateMessageOnDevice.tsx index a29b31efd1ac..e667cd03f8f9 100644 --- a/apps/ledger-live-mobile/src/components/ValidateMessageOnDevice.tsx +++ b/apps/ledger-live-mobile/src/components/ValidateMessageOnDevice.tsx @@ -1,14 +1,14 @@ -import { View, StyleSheet, ScrollView } from "react-native"; +import { getMessageProperties } from "@ledgerhq/coin-evm/logic"; +import { getMainAccount } from "@ledgerhq/live-common/account/helpers"; +import type { Device } from "@ledgerhq/live-common/hw/actions/types"; +import type { AccountLike, AnyMessage, MessageProperties } from "@ledgerhq/types-live"; +import { useTheme } from "@react-navigation/native"; import React, { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; -import type { AccountLike, AnyMessage } from "@ledgerhq/types-live"; -import { useTheme } from "@react-navigation/native"; -import type { Device } from "@ledgerhq/live-common/hw/actions/types"; -import { getMainAccount } from "@ledgerhq/live-common/account/helpers"; -import LText from "./LText"; -import Animation from "./Animation"; +import { ScrollView, StyleSheet, View } from "react-native"; import { getDeviceAnimation } from "../helpers/getDeviceAnimation"; -import { getMessageProperties, MessageProperties } from "../helpers/signMessageUtils"; +import Animation from "./Animation"; +import LText from "./LText"; type Props = { device: Device; @@ -44,7 +44,7 @@ export default function ValidateOnDevice({ device, message: messageData, account useEffect(() => { if (messageData.standard === "EIP712") { - getMessageProperties(mainAccount, messageData).then(setMessageFields); + getMessageProperties(messageData).then(setMessageFields); } }, [mainAccount, mainAccount.currency, messageData, setMessageFields]); diff --git a/apps/ledger-live-mobile/src/components/Web3AppWebview/liveSDKLogic.test.ts b/apps/ledger-live-mobile/src/components/Web3AppWebview/liveSDKLogic.test.ts index 966588908bbe..26b99364ac3b 100644 --- a/apps/ledger-live-mobile/src/components/Web3AppWebview/liveSDKLogic.test.ts +++ b/apps/ledger-live-mobile/src/components/Web3AppWebview/liveSDKLogic.test.ts @@ -7,6 +7,7 @@ import { } from "@ledgerhq/types-cryptoassets"; import { Account, TokenAccount } from "@ledgerhq/types-live"; import { Transaction } from "@ledgerhq/live-common/generated/types"; +import { Transaction as EvmTransaction } from "@ledgerhq/coin-evm/types/index"; import prepareSignTransaction from "./liveSDKLogic"; @@ -19,25 +20,24 @@ describe("prepareSignTransaction", () => { it("returns a Transaction", () => { // Given const parentAccount = createAccount("12"); - const childAccount = createTokenAccount("22", "ethereumjs:2:ethereum:0x012:"); - const expectedResult = { + const childAccount = createTokenAccount("22", "js:2:ethereum:0x012:"); + const expectedResult: EvmTransaction = { amount: new BigNumber("1000"), data: Buffer.from([]), - estimatedGasLimit: null, - family: "ethereum", - feeCustomUnit: { code: "Gwei", magnitude: 9, name: "Gwei" }, + family: "evm", feesStrategy: "medium", gasPrice: new BigNumber("700000"), gasLimit: new BigNumber("1200000"), - userGasLimit: new BigNumber("1200000"), + customGasLimit: new BigNumber("1200000"), mode: "send", - networkInfo: null, nonce: 8, recipient: "0x0123456", - subAccountId: "ethereumjs:2:ethereum:0x022:", + subAccountId: "js:2:ethereum:0x022:", useAllAmount: false, - maxFeePerGas: null, - maxPriorityFeePerGas: null, + maxFeePerGas: undefined, + maxPriorityFeePerGas: undefined, + type: 1, + chainId: 0, }; // When @@ -51,7 +51,7 @@ describe("prepareSignTransaction", () => { // *** UTIL FUNCTIONS *** function createEtherumTransaction(): Partial { return { - family: "ethereum", + family: "evm", amount: new BigNumber("1000"), recipient: "0x0123456", nonce: 8, @@ -68,7 +68,7 @@ const createCryptoCurrency = (family: string): CryptoCurrency => ({ name: "ethereum", managerAppName: "ethereum", ticker: "MYC", - scheme: "ethereum", + scheme: "evm", color: "#ff0000", family, units: [ @@ -92,10 +92,10 @@ const createCryptoCurrency = (family: string): CryptoCurrency => ({ ], }); -const defaultEthCryptoFamily = createCryptoCurrency("ethereum"); +const defaultEthCryptoFamily = createCryptoCurrency("evm"); const createAccount = (id: string, crypto: CryptoCurrency = defaultEthCryptoFamily): Account => ({ type: "Account", - id: `ethereumjs:2:ethereum:0x0${id}:`, + id: `js:2:ethereum:0x0${id}:`, seedIdentifier: "0x01", derivationMode: "ethM", index: 0, @@ -139,7 +139,7 @@ const createAccount = (id: string, crypto: CryptoCurrency = defaultEthCryptoFami function createTokenAccount(id = "32", parentId = "whatever"): TokenAccount { return { type: "TokenAccount", - id: `ethereumjs:2:ethereum:0x0${id}:`, + id: `js:2:ethereum:0x0${id}:`, parentId, token: createTokenCurrency(), balance: new BigNumber(0), diff --git a/apps/ledger-live-mobile/src/components/Web3AppWebview/liveSDKLogic.ts b/apps/ledger-live-mobile/src/components/Web3AppWebview/liveSDKLogic.ts index 635463916f1b..74b2228535ea 100644 --- a/apps/ledger-live-mobile/src/components/Web3AppWebview/liveSDKLogic.ts +++ b/apps/ledger-live-mobile/src/components/Web3AppWebview/liveSDKLogic.ts @@ -18,7 +18,10 @@ export default function prepareSignTransaction( }); return bridge.updateTransaction(t2, { - userGasLimit: txData.gasLimit, + customGasLimit: txData.gasLimit, + type: 1, + maxFeePerGas: undefined, + maxPriorityFeePerGas: undefined, ...txData, }); } diff --git a/apps/ledger-live-mobile/src/const/navigation.ts b/apps/ledger-live-mobile/src/const/navigation.ts index 80ce51f6709e..a4a7aba1f71c 100644 --- a/apps/ledger-live-mobile/src/const/navigation.ts +++ b/apps/ledger-live-mobile/src/const/navigation.ts @@ -331,11 +331,6 @@ export enum ScreenName { AlgorandOptInValidationError = "AlgorandOptInValidationError", AlgorandOptInValidationSuccess = "AlgorandOptInValidationSuccess", - // Ethereum - EthereumCustomFees = "EthereumCustomFees", - EthereumEditGasLimit = "EthereumEditGasLimit", - EthereumEditTransactionSummary = "EthereumEditTransactionSummary", - // Evm EvmEditGasLimit = "EvmEditGasLimit", EvmCustomFees = "EvmCustomFees", diff --git a/apps/ledger-live-mobile/src/families/ethereum/CurrentNetworkFee.tsx b/apps/ledger-live-mobile/src/families/ethereum/CurrentNetworkFee.tsx deleted file mode 100644 index 3c20ca643f67..000000000000 --- a/apps/ledger-live-mobile/src/families/ethereum/CurrentNetworkFee.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import React, { memo } from "react"; -import { TransactionRaw } from "@ledgerhq/live-common/families/ethereum/types"; -import BigNumber from "bignumber.js"; -import { - EIP1559ShouldBeUsed, - fromTransactionRaw, -} from "@ledgerhq/live-common/families/ethereum/transaction"; -import { useTranslation } from "react-i18next"; -import type { Account, AccountLike, TransactionCommonRaw } from "@ledgerhq/types-live"; -import { getMainAccount } from "@ledgerhq/live-common/account/index"; -import { View } from "react-native"; -import { log } from "@ledgerhq/logs"; - -import Alert from "../../components/Alert"; -import LText from "../../components/LText"; - -const CurrentNetworkFeeComponent = ({ - account, - parentAccount, - transactionRaw, - advancedMode = false, -}: { - account: AccountLike; - parentAccount: Account | null | undefined; - transactionRaw?: TransactionCommonRaw; - advancedMode?: boolean; -}) => { - const { t } = useTranslation(); - - if (transactionRaw) { - const transaction = fromTransactionRaw(transactionRaw as TransactionRaw); - - log("Edit Transaction", `transactionRaw.maxFeePerGas: ${transaction.maxFeePerGas}`); - log("Edit Transaction", `transactionRaw.gasPrice: ${transaction.gasPrice}`); - log( - "Edit Transaction", - `transactionRaw.maxPriorityFeePerGas: ${transaction.maxPriorityFeePerGas}`, - ); - - const mainAccount = getMainAccount(account, parentAccount); - const feePerGas = new BigNumber( - EIP1559ShouldBeUsed(mainAccount.currency) ? transaction.maxFeePerGas! : transaction.gasPrice!, - ); - - const feeValue = new BigNumber(transaction.userGasLimit || transaction.estimatedGasLimit || 1) - .times(feePerGas) - .div(new BigNumber(10).pow(mainAccount.unit.magnitude)); - - if (advancedMode) { - const maxPriorityFeePerGasinGwei = new BigNumber(transaction.maxPriorityFeePerGas ?? 0) - .dividedBy(1_000_000_000) - .toFixed(); - - const maxFeePerGasinGwei = new BigNumber(transaction.maxFeePerGas ?? 0) - .dividedBy(1_000_000_000) - .toFixed(); - - const maxGasPriceinGwei = new BigNumber(transaction?.gasPrice ?? 0) - .dividedBy(1_000_000_000) - .toFixed(); - - return ( - - {EIP1559ShouldBeUsed(mainAccount.currency) ? ( - - - {t("editTransaction.previousFeesInfo.maxPriorityFee", { - amount: maxPriorityFeePerGasinGwei, - })} - - - - {t("editTransaction.previousFeesInfo.maxFee", { - amount: maxFeePerGasinGwei, - })} - - - ) : ( - - {t("editTransaction.previousFeesInfo.gasPrice", { - amount: maxGasPriceinGwei, - })} - - )} - - ); - } - - return feeValue.toNumber() > 0 ? ( - - - {t("editTransaction.previousFeesInfo.networkfee", { - amount: feeValue.toNumber(), - unit: mainAccount.currency.ticker, - })} - - - ) : null; - } - - return null; -}; - -export const CurrentNetworkFee = memo(CurrentNetworkFeeComponent); diff --git a/apps/ledger-live-mobile/src/families/ethereum/EditFeeUnitEthereum.tsx b/apps/ledger-live-mobile/src/families/ethereum/EditFeeUnitEthereum.tsx deleted file mode 100644 index 8dfe84fb6605..000000000000 --- a/apps/ledger-live-mobile/src/families/ethereum/EditFeeUnitEthereum.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import React, { useCallback } from "react"; -import Slider from "react-native-slider"; -import { BigNumber } from "bignumber.js"; -import { useTranslation } from "react-i18next"; -import { View, StyleSheet } from "react-native"; -import { useTheme } from "@react-navigation/native"; -import type { Account, AccountLike } from "@ledgerhq/types-live"; -import { getMainAccount } from "@ledgerhq/live-common/account/index"; -import type { Transaction } from "@ledgerhq/live-common/families/ethereum/types"; -import { getDefaultFeeUnit } from "@ledgerhq/live-common/families/ethereum/logic"; -import { reverseRangeIndex, projectRangeIndex, Range } from "@ledgerhq/live-common/range"; -import LText from "../../components/LText"; -import CurrencyUnitValue from "../../components/CurrencyUnitValue"; - -const FeeSlider = React.memo( - ({ - value, - onChange, - range, - }: { - value: BigNumber; - onChange: (arg: unknown) => void; - range: Range; - }) => { - const { colors } = useTheme(); - const index = reverseRangeIndex(range, value); - const setValueIndex = useCallback( - i => onChange(projectRangeIndex(range, i)), - [range, onChange], - ); - return ( - - ); - }, -); - -type Props = { - account: AccountLike; - parentAccount: Account | null | undefined; - transaction: Transaction; - feeAmount: BigNumber; - onChange: (value: BigNumber) => void; - range: Range; - title: string; -}; -export default function EditFeeUnitEthereum({ - account, - parentAccount, - transaction, - feeAmount, - onChange, - range, - title, -}: Props) { - const { colors } = useTheme(); - const { t } = useTranslation(); - const mainAccount = getMainAccount(account, parentAccount); - const unit = getDefaultFeeUnit(mainAccount.currency); - - const feeCustomUnit = transaction.feeCustomUnit; - - const onChangeF = useCallback( - value => { - onChange(value); - }, - [onChange], - ); - - return ( - - - - - {t(title)} - - - - - - - - - - - - {t("fees.speed.slow")} - - - {t("fees.speed.fast")} - - - - - - ); -} - -const styles = StyleSheet.create({ - sliderContainer: { - paddingLeft: 0, - }, - feeHeader: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - }, - feeLabel: { - fontSize: 20, - }, - feeAmount: { - paddingVertical: 4, - paddingHorizontal: 8, - }, - container: { - flexDirection: "column", - justifyContent: "center", - }, - textContainer: { - flexDirection: "row", - justifyContent: "space-between", - }, - currencyUnitText: { - fontSize: 14, - textTransform: "capitalize", - }, -}); diff --git a/apps/ledger-live-mobile/src/families/ethereum/EditTransactionFlow/EditEthereumSummary.tsx b/apps/ledger-live-mobile/src/families/ethereum/EditTransactionFlow/EditEthereumSummary.tsx deleted file mode 100644 index d25c19f226c5..000000000000 --- a/apps/ledger-live-mobile/src/families/ethereum/EditTransactionFlow/EditEthereumSummary.tsx +++ /dev/null @@ -1,362 +0,0 @@ -import useBridgeTransaction from "@ledgerhq/live-common/bridge/useBridgeTransaction"; -import React, { useState, useCallback, Component, useEffect } from "react"; -import { View, StyleSheet } from "react-native"; -import { SafeAreaView } from "react-native-safe-area-context"; -import { useSelector } from "react-redux"; -import { Trans } from "react-i18next"; -import { getMainAccount, getAccountCurrency } from "@ledgerhq/live-common/account/index"; -import type { Account } from "@ledgerhq/types-live"; -import { isNftTransaction } from "@ledgerhq/live-common/nft/index"; -import { isEditableOperation } from "@ledgerhq/coin-framework/operation"; -import { NotEnoughGas, TransactionHasBeenValidatedError } from "@ledgerhq/errors"; -import { useNavigation, useTheme } from "@react-navigation/native"; -import invariant from "invariant"; -import { apiForCurrency } from "@ledgerhq/live-common/families/ethereum/api/index"; -import { isCurrencySupported } from "@ledgerhq/coin-framework/currencies/index"; - -import { NavigatorName, ScreenName } from "../../../const"; -import { accountScreenSelector } from "../../../reducers/accounts"; -import { TrackScreen } from "../../../analytics"; -import NavigationScrollView from "../../../components/NavigationScrollView"; -import Alert from "../../../components/Alert"; -import SummaryFromSection from "../../../screens/SendFunds/SummaryFromSection"; -import SummaryToSection from "../../../screens/SendFunds/SummaryToSection"; -import LText from "../../../components/LText"; -import SectionSeparator from "../../../components/SectionSeparator"; -import SummaryNft from "../../../screens/SendFunds/SummaryNft"; -import SummaryAmountSection from "../../../screens/SendFunds/SummaryAmountSection"; -import TranslatedError from "../../../components/TranslatedError"; -import SendRowsFee from "../../../components/SendRowsFee"; -import SendRowsCustom from "../../../components/SendRowsCustom"; -import Info from "../../../icons/Info"; -import SummaryTotalSection from "../../../screens/SendFunds/SummaryTotalSection"; -import Button from "../../../components/Button"; -import ConfirmationModal from "../../../components/ConfirmationModal"; -import AlertTriangle from "../../../icons/AlertTriangle"; -import TooMuchUTXOBottomModal from "../../../screens/SendFunds/TooMuchUTXOBottomModal"; -import { CurrentNetworkFee } from "../CurrentNetworkFee"; -import { useTransactionChangeFromNavigation } from "../../../logic/screenTransactionHooks"; -import { CryptoCurrency } from "@ledgerhq/types-cryptoassets"; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function EditEthereumSummary({ navigation, route }: any) { - const { colors } = useTheme(); - const { nextNavigation, overrideAmountLabel, hideTotal, operation } = route.params; - - const { account, parentAccount } = useSelector(accountScreenSelector(route)); - - invariant(account, "account is missing"); - - const { transaction, setTransaction, status, bridgePending } = useBridgeTransaction(() => ({ - transaction: route.params.transaction, - account, - parentAccount, - })); - - invariant(transaction, "transaction is missing"); - - const isNFTSend = isNftTransaction(transaction); - // handle any edit screen changes like fees changes - useTransactionChangeFromNavigation(setTransaction); - const [continuing, setContinuing] = useState(false); - const [highFeesOpen, setHighFeesOpen] = useState(false); - const [highFeesWarningPassed, setHighFeesWarningPassed] = useState(false); - const [utxoWarningOpen, setUtxoWarningOpen] = useState(false); - const [utxoWarningPassed, setUtxoWarningPassed] = useState(false); - const navigateToNext = useCallback(() => { - if (!nextNavigation) return null; - return ( - // This component is used in a wild bunch of navigators. - // nextNavigation is a param which can have too many shapes - // Unfortunately for this reason let's keep it untyped for now. - navigation.navigate(nextNavigation, { - ...route.params, - transaction, - status, - selectDeviceLink: true, - }) - ); - }, [navigation, nextNavigation, route.params, transaction, status]); - useEffect(() => { - if (!continuing) { - return; - } - const { warnings } = status; - - if (Object.keys(warnings).includes("feeTooHigh") && !highFeesWarningPassed) { - setHighFeesOpen(true); - return; - } - - setContinuing(false); - setUtxoWarningPassed(false); - setHighFeesWarningPassed(false); - navigateToNext(); - }, [status, continuing, highFeesWarningPassed, account, utxoWarningPassed, navigateToNext]); - const onPassUtxoWarning = useCallback(() => { - setUtxoWarningOpen(false); - setUtxoWarningPassed(true); - }, []); - const onRejectUtxoWarning = useCallback(() => { - setUtxoWarningOpen(false); - setContinuing(false); - }, []); - const onAcceptFees = useCallback(() => { - setHighFeesOpen(false); - setHighFeesWarningPassed(true); - }, []); - const onRejectFees = useCallback(() => { - setHighFeesOpen(false); - setContinuing(false); - }, [setHighFeesOpen]); - const { amount, totalSpent, errors } = status; - const { transaction: transactionError } = errors; - const error = errors[Object.keys(errors)[0]]; - const mainAccount = getMainAccount(account, parentAccount); - const currencyOrToken = getAccountCurrency(account); - const hasNonEmptySubAccounts = - account && - account.type === "Account" && - (account.subAccounts || []).some(subAccount => subAccount.balance.gt(0)); - - const onBuyEth = useCallback(() => { - navigation.navigate(NavigatorName.Exchange, { - screen: ScreenName.ExchangeBuy, - params: { - defaultAccountId: account?.id, - defaultCurrencyId: currencyOrToken?.id, - }, - }); - }, [navigation, account?.id, currencyOrToken?.id]); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const errorNavigation = useNavigation(); - const editableOperation = operation && isEditableOperation(account, operation); - - if (editableOperation) { - apiForCurrency(mainAccount.currency) - .getTransactionByHash(operation?.hash || "") - .then(tx => { - if (tx?.confirmations) { - errorNavigation.navigate(ScreenName.TransactionAlreadyValidatedError, { - error: new TransactionHasBeenValidatedError( - "The transaction has already been validated. You can't cancel or speedup a validated transaction.", - ), - }); - } - }); - } - - // FIXME: why is recipient sometimes empty? - if (!account || !transaction || !transaction.recipient || !currencyOrToken) { - return null; - } - - return ( - - - - {transaction.useAllAmount && hasNonEmptySubAccounts ? ( - - - - - - ) : null} - - - - {status.warnings.recipient ? ( - - - - ) : null} - - - {isNFTSend ? ( - - ) : ( - - )} - - - {editableOperation ? ( - - ) : null} - - {error ? ( - - - - - - - - - ) : null} - {!amount.eq(totalSpent) && !hideTotal ? ( - <> - - - - ) : null} - - - - - - {error && error instanceof NotEnoughGas ? ( - isCurrencySupported(currencyOrToken as CryptoCurrency) && ( -